@qazuor/claude-code-config 0.1.0 → 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/README.md +70 -16
- package/dist/bin.cjs +1434 -635
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1413 -614
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +195 -425
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +195 -425
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md.template +108 -0
package/dist/bin.cjs
CHANGED
|
@@ -72,11 +72,11 @@ __export(fs_exports, {
|
|
|
72
72
|
writeJson: () => writeJson
|
|
73
73
|
});
|
|
74
74
|
async function pathExists(filePath) {
|
|
75
|
-
return
|
|
75
|
+
return import_fs_extra.default.pathExists(filePath);
|
|
76
76
|
}
|
|
77
77
|
async function isDirectory(filePath) {
|
|
78
78
|
try {
|
|
79
|
-
const stat = await
|
|
79
|
+
const stat = await import_fs_extra.default.stat(filePath);
|
|
80
80
|
return stat.isDirectory();
|
|
81
81
|
} catch {
|
|
82
82
|
return false;
|
|
@@ -84,41 +84,41 @@ async function isDirectory(filePath) {
|
|
|
84
84
|
}
|
|
85
85
|
async function isFile(filePath) {
|
|
86
86
|
try {
|
|
87
|
-
const stat = await
|
|
87
|
+
const stat = await import_fs_extra.default.stat(filePath);
|
|
88
88
|
return stat.isFile();
|
|
89
89
|
} catch {
|
|
90
90
|
return false;
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
async function readJson(filePath) {
|
|
94
|
-
return
|
|
94
|
+
return import_fs_extra.default.readJson(filePath);
|
|
95
95
|
}
|
|
96
96
|
async function writeJson(filePath, data, options) {
|
|
97
|
-
await
|
|
98
|
-
await
|
|
97
|
+
await import_fs_extra.default.ensureDir(import_node_path.default.dirname(filePath));
|
|
98
|
+
await import_fs_extra.default.writeJson(filePath, data, { spaces: options?.spaces ?? 2 });
|
|
99
99
|
}
|
|
100
100
|
async function readFile(filePath) {
|
|
101
|
-
return
|
|
101
|
+
return import_fs_extra.default.readFile(filePath, "utf-8");
|
|
102
102
|
}
|
|
103
103
|
async function writeFile(filePath, content) {
|
|
104
|
-
await
|
|
105
|
-
await
|
|
104
|
+
await import_fs_extra.default.ensureDir(import_node_path.default.dirname(filePath));
|
|
105
|
+
await import_fs_extra.default.writeFile(filePath, content, "utf-8");
|
|
106
106
|
}
|
|
107
107
|
async function copy(src, dest, options) {
|
|
108
|
-
await
|
|
109
|
-
await
|
|
108
|
+
await import_fs_extra.default.ensureDir(import_node_path.default.dirname(dest));
|
|
109
|
+
await import_fs_extra.default.copy(src, dest, { overwrite: options?.overwrite ?? false });
|
|
110
110
|
}
|
|
111
111
|
async function copyDir(src, dest, options) {
|
|
112
|
-
await
|
|
112
|
+
await import_fs_extra.default.copy(src, dest, {
|
|
113
113
|
overwrite: options?.overwrite ?? false,
|
|
114
114
|
filter: options?.filter
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
117
|
async function ensureDir(dirPath) {
|
|
118
|
-
await
|
|
118
|
+
await import_fs_extra.default.ensureDir(dirPath);
|
|
119
119
|
}
|
|
120
120
|
async function remove(filePath) {
|
|
121
|
-
await
|
|
121
|
+
await import_fs_extra.default.remove(filePath);
|
|
122
122
|
}
|
|
123
123
|
async function listFiles(pattern, options) {
|
|
124
124
|
return (0, import_glob.glob)(pattern, {
|
|
@@ -133,7 +133,7 @@ async function listDirs(pattern, options) {
|
|
|
133
133
|
});
|
|
134
134
|
const dirs = [];
|
|
135
135
|
for (const match of matches) {
|
|
136
|
-
const fullPath = options?.cwd ?
|
|
136
|
+
const fullPath = options?.cwd ? import_node_path.default.join(options.cwd, match) : match;
|
|
137
137
|
if (await isDirectory(fullPath)) {
|
|
138
138
|
dirs.push(match);
|
|
139
139
|
}
|
|
@@ -142,7 +142,7 @@ async function listDirs(pattern, options) {
|
|
|
142
142
|
}
|
|
143
143
|
async function getFileInfo(filePath) {
|
|
144
144
|
try {
|
|
145
|
-
const stat = await
|
|
145
|
+
const stat = await import_fs_extra.default.stat(filePath);
|
|
146
146
|
return {
|
|
147
147
|
exists: true,
|
|
148
148
|
isFile: stat.isFile(),
|
|
@@ -163,7 +163,7 @@ async function readDirRecursive(dirPath, options) {
|
|
|
163
163
|
}
|
|
164
164
|
async function getFileHash(filePath) {
|
|
165
165
|
const crypto = await import("crypto");
|
|
166
|
-
const content = await
|
|
166
|
+
const content = await import_fs_extra.default.readFile(filePath);
|
|
167
167
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
168
168
|
}
|
|
169
169
|
async function filesAreEqual(file1, file2) {
|
|
@@ -175,22 +175,22 @@ async function filesAreEqual(file1, file2) {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
function relativePath(from, to) {
|
|
178
|
-
return
|
|
178
|
+
return import_node_path.default.relative(from, to);
|
|
179
179
|
}
|
|
180
180
|
function resolvePath(...segments) {
|
|
181
|
-
return
|
|
181
|
+
return import_node_path.default.resolve(...segments);
|
|
182
182
|
}
|
|
183
183
|
function joinPath(...segments) {
|
|
184
|
-
return
|
|
184
|
+
return import_node_path.default.join(...segments);
|
|
185
185
|
}
|
|
186
186
|
function dirname(filePath) {
|
|
187
|
-
return
|
|
187
|
+
return import_node_path.default.dirname(filePath);
|
|
188
188
|
}
|
|
189
189
|
function basename(filePath, ext) {
|
|
190
|
-
return
|
|
190
|
+
return import_node_path.default.basename(filePath, ext);
|
|
191
191
|
}
|
|
192
192
|
function extname(filePath) {
|
|
193
|
-
return
|
|
193
|
+
return import_node_path.default.extname(filePath);
|
|
194
194
|
}
|
|
195
195
|
async function backup(src, suffix = ".backup") {
|
|
196
196
|
const backupPath = `${src}${suffix}`;
|
|
@@ -199,27 +199,27 @@ async function backup(src, suffix = ".backup") {
|
|
|
199
199
|
}
|
|
200
200
|
async function makeExecutable(filePath) {
|
|
201
201
|
try {
|
|
202
|
-
await
|
|
202
|
+
await import_fs_extra.default.chmod(filePath, 493);
|
|
203
203
|
} catch {
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
async function createTempDir(prefix = "claude-config-") {
|
|
207
207
|
const os4 = await import("os");
|
|
208
208
|
const tempBase = os4.tmpdir();
|
|
209
|
-
const tempDir =
|
|
209
|
+
const tempDir = import_node_path.default.join(tempBase, `${prefix}${Date.now()}`);
|
|
210
210
|
await ensureDir(tempDir);
|
|
211
211
|
return tempDir;
|
|
212
212
|
}
|
|
213
213
|
async function cleanTempDir(tempDir) {
|
|
214
214
|
await remove(tempDir);
|
|
215
215
|
}
|
|
216
|
-
var
|
|
216
|
+
var import_node_path, import_fs_extra, import_glob;
|
|
217
217
|
var init_fs = __esm({
|
|
218
218
|
"src/lib/utils/fs.ts"() {
|
|
219
219
|
"use strict";
|
|
220
220
|
init_cjs_shims();
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
import_node_path = __toESM(require("path"), 1);
|
|
222
|
+
import_fs_extra = __toESM(require("fs-extra"), 1);
|
|
223
223
|
import_glob = require("glob");
|
|
224
224
|
}
|
|
225
225
|
});
|
|
@@ -1350,13 +1350,351 @@ function getBundleCategoryName(category) {
|
|
|
1350
1350
|
return BUNDLE_CATEGORY_NAMES[category] ?? category;
|
|
1351
1351
|
}
|
|
1352
1352
|
|
|
1353
|
+
// src/lib/ci-cd/index.ts
|
|
1354
|
+
init_cjs_shims();
|
|
1355
|
+
|
|
1356
|
+
// src/lib/ci-cd/github-actions-generator.ts
|
|
1357
|
+
init_cjs_shims();
|
|
1358
|
+
init_fs();
|
|
1359
|
+
|
|
1360
|
+
// src/lib/utils/spinner.ts
|
|
1361
|
+
init_cjs_shims();
|
|
1362
|
+
var import_ora = __toESM(require("ora"), 1);
|
|
1363
|
+
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
1364
|
+
var SpinnerManager = class {
|
|
1365
|
+
spinner = null;
|
|
1366
|
+
silent = false;
|
|
1367
|
+
configure(options) {
|
|
1368
|
+
if (options.silent !== void 0) this.silent = options.silent;
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Start a spinner with a message
|
|
1372
|
+
*/
|
|
1373
|
+
start(text, options) {
|
|
1374
|
+
if (this.silent) return null;
|
|
1375
|
+
this.stop();
|
|
1376
|
+
this.spinner = (0, import_ora.default)({
|
|
1377
|
+
text,
|
|
1378
|
+
color: options?.color || "cyan",
|
|
1379
|
+
spinner: "dots"
|
|
1380
|
+
}).start();
|
|
1381
|
+
return this.spinner;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Update spinner text
|
|
1385
|
+
*/
|
|
1386
|
+
text(text) {
|
|
1387
|
+
if (this.spinner) {
|
|
1388
|
+
this.spinner.text = text;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* Stop spinner with success message
|
|
1393
|
+
*/
|
|
1394
|
+
succeed(text) {
|
|
1395
|
+
if (this.spinner) {
|
|
1396
|
+
this.spinner.succeed(text);
|
|
1397
|
+
this.spinner = null;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Stop spinner with failure message
|
|
1402
|
+
*/
|
|
1403
|
+
fail(text) {
|
|
1404
|
+
if (this.spinner) {
|
|
1405
|
+
this.spinner.fail(text);
|
|
1406
|
+
this.spinner = null;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Stop spinner with warning message
|
|
1411
|
+
*/
|
|
1412
|
+
warn(text) {
|
|
1413
|
+
if (this.spinner) {
|
|
1414
|
+
this.spinner.warn(text);
|
|
1415
|
+
this.spinner = null;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Stop spinner with info message
|
|
1420
|
+
*/
|
|
1421
|
+
info(text) {
|
|
1422
|
+
if (this.spinner) {
|
|
1423
|
+
this.spinner.info(text);
|
|
1424
|
+
this.spinner = null;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Stop spinner without message
|
|
1429
|
+
*/
|
|
1430
|
+
stop() {
|
|
1431
|
+
if (this.spinner) {
|
|
1432
|
+
this.spinner.stop();
|
|
1433
|
+
this.spinner = null;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Check if spinner is running
|
|
1438
|
+
*/
|
|
1439
|
+
isRunning() {
|
|
1440
|
+
return this.spinner?.isSpinning ?? false;
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
var spinner = new SpinnerManager();
|
|
1444
|
+
async function withSpinner(text, operation, options) {
|
|
1445
|
+
if (options?.silent) {
|
|
1446
|
+
return operation();
|
|
1447
|
+
}
|
|
1448
|
+
spinner.start(text);
|
|
1449
|
+
try {
|
|
1450
|
+
const result = await operation();
|
|
1451
|
+
spinner.succeed(options?.successText || text);
|
|
1452
|
+
return result;
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1455
|
+
spinner.fail(options?.failText || `${text} - ${import_picocolors.default.red(errorMessage)}`);
|
|
1456
|
+
throw error;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// src/lib/ci-cd/github-actions-generator.ts
|
|
1461
|
+
function getInstallCommand(packageManager) {
|
|
1462
|
+
switch (packageManager) {
|
|
1463
|
+
case "npm":
|
|
1464
|
+
return "npm ci";
|
|
1465
|
+
case "yarn":
|
|
1466
|
+
return "yarn install --frozen-lockfile";
|
|
1467
|
+
case "pnpm":
|
|
1468
|
+
return "pnpm install --frozen-lockfile";
|
|
1469
|
+
case "bun":
|
|
1470
|
+
return "bun install --frozen-lockfile";
|
|
1471
|
+
default:
|
|
1472
|
+
return "npm ci";
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
function getRunCommand(packageManager, script) {
|
|
1476
|
+
switch (packageManager) {
|
|
1477
|
+
case "npm":
|
|
1478
|
+
return `npm run ${script}`;
|
|
1479
|
+
case "yarn":
|
|
1480
|
+
return `yarn ${script}`;
|
|
1481
|
+
case "pnpm":
|
|
1482
|
+
return `pnpm ${script}`;
|
|
1483
|
+
case "bun":
|
|
1484
|
+
return `bun run ${script}`;
|
|
1485
|
+
default:
|
|
1486
|
+
return `npm run ${script}`;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
function getCacheConfig(packageManager) {
|
|
1490
|
+
switch (packageManager) {
|
|
1491
|
+
case "npm":
|
|
1492
|
+
return {
|
|
1493
|
+
path: "~/.npm",
|
|
1494
|
+
key: "npm-${{ hashFiles('**/package-lock.json') }}"
|
|
1495
|
+
};
|
|
1496
|
+
case "yarn":
|
|
1497
|
+
return {
|
|
1498
|
+
path: ".yarn/cache",
|
|
1499
|
+
key: "yarn-${{ hashFiles('**/yarn.lock') }}"
|
|
1500
|
+
};
|
|
1501
|
+
case "pnpm":
|
|
1502
|
+
return {
|
|
1503
|
+
path: "~/.local/share/pnpm/store",
|
|
1504
|
+
key: "pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}"
|
|
1505
|
+
};
|
|
1506
|
+
case "bun":
|
|
1507
|
+
return {
|
|
1508
|
+
path: "~/.bun/install/cache",
|
|
1509
|
+
key: "bun-${{ hashFiles('**/bun.lockb') }}"
|
|
1510
|
+
};
|
|
1511
|
+
default:
|
|
1512
|
+
return {
|
|
1513
|
+
path: "~/.npm",
|
|
1514
|
+
key: "npm-${{ hashFiles('**/package-lock.json') }}"
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
function generateCIWorkflow(config) {
|
|
1519
|
+
const { packageManager, nodeVersion, enableCaching, runTests, runLint, runTypecheck, runBuild } = config;
|
|
1520
|
+
const cache = getCacheConfig(packageManager);
|
|
1521
|
+
const installCmd = getInstallCommand(packageManager);
|
|
1522
|
+
const steps = [];
|
|
1523
|
+
steps.push(` - name: Checkout
|
|
1524
|
+
uses: actions/checkout@v4`);
|
|
1525
|
+
if (packageManager === "pnpm") {
|
|
1526
|
+
steps.push(`
|
|
1527
|
+
- name: Setup pnpm
|
|
1528
|
+
uses: pnpm/action-setup@v4
|
|
1529
|
+
with:
|
|
1530
|
+
version: latest`);
|
|
1531
|
+
}
|
|
1532
|
+
if (packageManager === "bun") {
|
|
1533
|
+
steps.push(`
|
|
1534
|
+
- name: Setup Bun
|
|
1535
|
+
uses: oven-sh/setup-bun@v2
|
|
1536
|
+
with:
|
|
1537
|
+
bun-version: latest`);
|
|
1538
|
+
}
|
|
1539
|
+
if (packageManager !== "bun") {
|
|
1540
|
+
steps.push(`
|
|
1541
|
+
- name: Setup Node.js
|
|
1542
|
+
uses: actions/setup-node@v4
|
|
1543
|
+
with:
|
|
1544
|
+
node-version: '${nodeVersion}'${enableCaching ? `
|
|
1545
|
+
cache: '${packageManager}'` : ""}`);
|
|
1546
|
+
}
|
|
1547
|
+
if (enableCaching && packageManager === "bun") {
|
|
1548
|
+
steps.push(`
|
|
1549
|
+
- name: Cache dependencies
|
|
1550
|
+
uses: actions/cache@v4
|
|
1551
|
+
with:
|
|
1552
|
+
path: ${cache.path}
|
|
1553
|
+
key: ${cache.key}`);
|
|
1554
|
+
}
|
|
1555
|
+
steps.push(`
|
|
1556
|
+
- name: Install dependencies
|
|
1557
|
+
run: ${installCmd}`);
|
|
1558
|
+
if (runLint) {
|
|
1559
|
+
steps.push(`
|
|
1560
|
+
- name: Lint
|
|
1561
|
+
run: ${getRunCommand(packageManager, "lint")}`);
|
|
1562
|
+
}
|
|
1563
|
+
if (runTypecheck) {
|
|
1564
|
+
steps.push(`
|
|
1565
|
+
- name: Type check
|
|
1566
|
+
run: ${getRunCommand(packageManager, "typecheck")}`);
|
|
1567
|
+
}
|
|
1568
|
+
if (runTests) {
|
|
1569
|
+
steps.push(`
|
|
1570
|
+
- name: Run tests
|
|
1571
|
+
run: ${getRunCommand(packageManager, "test")}`);
|
|
1572
|
+
}
|
|
1573
|
+
if (runBuild) {
|
|
1574
|
+
steps.push(`
|
|
1575
|
+
- name: Build
|
|
1576
|
+
run: ${getRunCommand(packageManager, "build")}`);
|
|
1577
|
+
}
|
|
1578
|
+
return `name: CI
|
|
1579
|
+
|
|
1580
|
+
on:
|
|
1581
|
+
push:
|
|
1582
|
+
branches: [main, master]
|
|
1583
|
+
pull_request:
|
|
1584
|
+
branches: [main, master]
|
|
1585
|
+
|
|
1586
|
+
jobs:
|
|
1587
|
+
ci:
|
|
1588
|
+
runs-on: ubuntu-latest
|
|
1589
|
+
|
|
1590
|
+
steps:
|
|
1591
|
+
${steps.join("\n")}
|
|
1592
|
+
`;
|
|
1593
|
+
}
|
|
1594
|
+
function generateReleaseWorkflow(config) {
|
|
1595
|
+
const { packageManager, nodeVersion } = config;
|
|
1596
|
+
const installCmd = getInstallCommand(packageManager);
|
|
1597
|
+
let setupSteps = "";
|
|
1598
|
+
if (packageManager === "pnpm") {
|
|
1599
|
+
setupSteps = `
|
|
1600
|
+
- name: Setup pnpm
|
|
1601
|
+
uses: pnpm/action-setup@v4
|
|
1602
|
+
with:
|
|
1603
|
+
version: latest
|
|
1604
|
+
`;
|
|
1605
|
+
}
|
|
1606
|
+
return `name: Release
|
|
1607
|
+
|
|
1608
|
+
on:
|
|
1609
|
+
push:
|
|
1610
|
+
tags:
|
|
1611
|
+
- 'v*'
|
|
1612
|
+
|
|
1613
|
+
jobs:
|
|
1614
|
+
release:
|
|
1615
|
+
runs-on: ubuntu-latest
|
|
1616
|
+
permissions:
|
|
1617
|
+
contents: write
|
|
1618
|
+
|
|
1619
|
+
steps:
|
|
1620
|
+
- name: Checkout
|
|
1621
|
+
uses: actions/checkout@v4
|
|
1622
|
+
with:
|
|
1623
|
+
fetch-depth: 0
|
|
1624
|
+
${setupSteps}
|
|
1625
|
+
- name: Setup Node.js
|
|
1626
|
+
uses: actions/setup-node@v4
|
|
1627
|
+
with:
|
|
1628
|
+
node-version: '${nodeVersion}'
|
|
1629
|
+
cache: '${packageManager}'
|
|
1630
|
+
registry-url: 'https://registry.npmjs.org'
|
|
1631
|
+
|
|
1632
|
+
- name: Install dependencies
|
|
1633
|
+
run: ${installCmd}
|
|
1634
|
+
|
|
1635
|
+
- name: Build
|
|
1636
|
+
run: ${getRunCommand(packageManager, "build")}
|
|
1637
|
+
|
|
1638
|
+
- name: Create GitHub Release
|
|
1639
|
+
uses: softprops/action-gh-release@v1
|
|
1640
|
+
with:
|
|
1641
|
+
generate_release_notes: true
|
|
1642
|
+
`;
|
|
1643
|
+
}
|
|
1644
|
+
async function installCICD(projectPath, config, options) {
|
|
1645
|
+
const result = {
|
|
1646
|
+
created: [],
|
|
1647
|
+
skipped: [],
|
|
1648
|
+
errors: []
|
|
1649
|
+
};
|
|
1650
|
+
if (!config.enabled) {
|
|
1651
|
+
return result;
|
|
1652
|
+
}
|
|
1653
|
+
const workflowsDir = joinPath(projectPath, ".github", "workflows");
|
|
1654
|
+
try {
|
|
1655
|
+
await ensureDir(workflowsDir);
|
|
1656
|
+
if (config.ci) {
|
|
1657
|
+
const ciPath = joinPath(workflowsDir, "ci.yml");
|
|
1658
|
+
if (!await pathExists(ciPath) || options?.overwrite) {
|
|
1659
|
+
const content = generateCIWorkflow(config);
|
|
1660
|
+
await writeFile(ciPath, content);
|
|
1661
|
+
result.created.push("ci.yml");
|
|
1662
|
+
} else {
|
|
1663
|
+
result.skipped.push("ci.yml");
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
if (config.cd) {
|
|
1667
|
+
const releasePath = joinPath(workflowsDir, "release.yml");
|
|
1668
|
+
if (!await pathExists(releasePath) || options?.overwrite) {
|
|
1669
|
+
const content = generateReleaseWorkflow(config);
|
|
1670
|
+
await writeFile(releasePath, content);
|
|
1671
|
+
result.created.push("release.yml");
|
|
1672
|
+
} else {
|
|
1673
|
+
result.skipped.push("release.yml");
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
} catch (error) {
|
|
1677
|
+
result.errors.push(error instanceof Error ? error.message : String(error));
|
|
1678
|
+
}
|
|
1679
|
+
return result;
|
|
1680
|
+
}
|
|
1681
|
+
async function installCICDWithSpinner(projectPath, config, options) {
|
|
1682
|
+
return withSpinner(
|
|
1683
|
+
"Installing GitHub Actions workflows...",
|
|
1684
|
+
() => installCICD(projectPath, config, options),
|
|
1685
|
+
{
|
|
1686
|
+
successText: "Installed GitHub Actions workflows"
|
|
1687
|
+
}
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1353
1691
|
// src/lib/code-style/index.ts
|
|
1354
1692
|
init_cjs_shims();
|
|
1355
1693
|
|
|
1356
1694
|
// src/lib/code-style/installer.ts
|
|
1357
1695
|
init_cjs_shims();
|
|
1358
|
-
var
|
|
1359
|
-
var
|
|
1696
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
1697
|
+
var import_fs_extra2 = __toESM(require("fs-extra"), 1);
|
|
1360
1698
|
|
|
1361
1699
|
// src/constants/code-style-defaults.ts
|
|
1362
1700
|
init_cjs_shims();
|
|
@@ -1726,106 +2064,6 @@ var colors = {
|
|
|
1726
2064
|
underline: import_chalk.default.underline
|
|
1727
2065
|
};
|
|
1728
2066
|
|
|
1729
|
-
// src/lib/utils/spinner.ts
|
|
1730
|
-
init_cjs_shims();
|
|
1731
|
-
var import_ora = __toESM(require("ora"), 1);
|
|
1732
|
-
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
1733
|
-
var SpinnerManager = class {
|
|
1734
|
-
spinner = null;
|
|
1735
|
-
silent = false;
|
|
1736
|
-
configure(options) {
|
|
1737
|
-
if (options.silent !== void 0) this.silent = options.silent;
|
|
1738
|
-
}
|
|
1739
|
-
/**
|
|
1740
|
-
* Start a spinner with a message
|
|
1741
|
-
*/
|
|
1742
|
-
start(text, options) {
|
|
1743
|
-
if (this.silent) return null;
|
|
1744
|
-
this.stop();
|
|
1745
|
-
this.spinner = (0, import_ora.default)({
|
|
1746
|
-
text,
|
|
1747
|
-
color: options?.color || "cyan",
|
|
1748
|
-
spinner: "dots"
|
|
1749
|
-
}).start();
|
|
1750
|
-
return this.spinner;
|
|
1751
|
-
}
|
|
1752
|
-
/**
|
|
1753
|
-
* Update spinner text
|
|
1754
|
-
*/
|
|
1755
|
-
text(text) {
|
|
1756
|
-
if (this.spinner) {
|
|
1757
|
-
this.spinner.text = text;
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
/**
|
|
1761
|
-
* Stop spinner with success message
|
|
1762
|
-
*/
|
|
1763
|
-
succeed(text) {
|
|
1764
|
-
if (this.spinner) {
|
|
1765
|
-
this.spinner.succeed(text);
|
|
1766
|
-
this.spinner = null;
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
/**
|
|
1770
|
-
* Stop spinner with failure message
|
|
1771
|
-
*/
|
|
1772
|
-
fail(text) {
|
|
1773
|
-
if (this.spinner) {
|
|
1774
|
-
this.spinner.fail(text);
|
|
1775
|
-
this.spinner = null;
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
/**
|
|
1779
|
-
* Stop spinner with warning message
|
|
1780
|
-
*/
|
|
1781
|
-
warn(text) {
|
|
1782
|
-
if (this.spinner) {
|
|
1783
|
-
this.spinner.warn(text);
|
|
1784
|
-
this.spinner = null;
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
/**
|
|
1788
|
-
* Stop spinner with info message
|
|
1789
|
-
*/
|
|
1790
|
-
info(text) {
|
|
1791
|
-
if (this.spinner) {
|
|
1792
|
-
this.spinner.info(text);
|
|
1793
|
-
this.spinner = null;
|
|
1794
|
-
}
|
|
1795
|
-
}
|
|
1796
|
-
/**
|
|
1797
|
-
* Stop spinner without message
|
|
1798
|
-
*/
|
|
1799
|
-
stop() {
|
|
1800
|
-
if (this.spinner) {
|
|
1801
|
-
this.spinner.stop();
|
|
1802
|
-
this.spinner = null;
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
/**
|
|
1806
|
-
* Check if spinner is running
|
|
1807
|
-
*/
|
|
1808
|
-
isRunning() {
|
|
1809
|
-
return this.spinner?.isSpinning ?? false;
|
|
1810
|
-
}
|
|
1811
|
-
};
|
|
1812
|
-
var spinner = new SpinnerManager();
|
|
1813
|
-
async function withSpinner(text, operation, options) {
|
|
1814
|
-
if (options?.silent) {
|
|
1815
|
-
return operation();
|
|
1816
|
-
}
|
|
1817
|
-
spinner.start(text);
|
|
1818
|
-
try {
|
|
1819
|
-
const result = await operation();
|
|
1820
|
-
spinner.succeed(options?.successText || text);
|
|
1821
|
-
return result;
|
|
1822
|
-
} catch (error) {
|
|
1823
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1824
|
-
spinner.fail(options?.failText || `${text} - ${import_picocolors.default.red(errorMessage)}`);
|
|
1825
|
-
throw error;
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
2067
|
// src/lib/code-style/generator.ts
|
|
1830
2068
|
init_cjs_shims();
|
|
1831
2069
|
function generateEditorConfig(options) {
|
|
@@ -2006,14 +2244,14 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
2006
2244
|
spinner.start("Installing code style configurations...");
|
|
2007
2245
|
if (config.editorconfig) {
|
|
2008
2246
|
const filename = CODE_STYLE_FILES.editorconfig;
|
|
2009
|
-
const destPath =
|
|
2247
|
+
const destPath = import_node_path2.default.join(targetPath, filename);
|
|
2010
2248
|
try {
|
|
2011
|
-
if (await
|
|
2249
|
+
if (await import_fs_extra2.default.pathExists(destPath) && !options?.overwrite) {
|
|
2012
2250
|
result.skipped.push(filename);
|
|
2013
2251
|
} else {
|
|
2014
2252
|
const editorconfigOptions = config.editorconfigOptions ?? DEFAULT_EDITORCONFIG_OPTIONS;
|
|
2015
2253
|
const content = generateEditorConfig(editorconfigOptions);
|
|
2016
|
-
await
|
|
2254
|
+
await import_fs_extra2.default.writeFile(destPath, content, "utf-8");
|
|
2017
2255
|
result.installed.push(filename);
|
|
2018
2256
|
}
|
|
2019
2257
|
} catch (error) {
|
|
@@ -2023,14 +2261,14 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
2023
2261
|
}
|
|
2024
2262
|
if (config.biome) {
|
|
2025
2263
|
const filename = CODE_STYLE_FILES.biome;
|
|
2026
|
-
const destPath =
|
|
2264
|
+
const destPath = import_node_path2.default.join(targetPath, filename);
|
|
2027
2265
|
try {
|
|
2028
|
-
if (await
|
|
2266
|
+
if (await import_fs_extra2.default.pathExists(destPath) && !options?.overwrite) {
|
|
2029
2267
|
result.skipped.push(filename);
|
|
2030
2268
|
} else {
|
|
2031
2269
|
const biomeOptions = config.biomeOptions ?? DEFAULT_BIOME_OPTIONS;
|
|
2032
2270
|
const content = generateBiomeConfig(biomeOptions);
|
|
2033
|
-
await
|
|
2271
|
+
await import_fs_extra2.default.writeFile(destPath, content, "utf-8");
|
|
2034
2272
|
result.installed.push(filename);
|
|
2035
2273
|
}
|
|
2036
2274
|
} catch (error) {
|
|
@@ -2040,20 +2278,20 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
2040
2278
|
}
|
|
2041
2279
|
if (config.prettier) {
|
|
2042
2280
|
const filename = CODE_STYLE_FILES.prettier;
|
|
2043
|
-
const destPath =
|
|
2281
|
+
const destPath = import_node_path2.default.join(targetPath, filename);
|
|
2044
2282
|
try {
|
|
2045
|
-
if (await
|
|
2283
|
+
if (await import_fs_extra2.default.pathExists(destPath) && !options?.overwrite) {
|
|
2046
2284
|
result.skipped.push(filename);
|
|
2047
2285
|
} else {
|
|
2048
2286
|
const prettierOptions = config.prettierOptions ?? DEFAULT_PRETTIER_OPTIONS;
|
|
2049
2287
|
const content = generatePrettierConfig(prettierOptions);
|
|
2050
|
-
await
|
|
2288
|
+
await import_fs_extra2.default.writeFile(destPath, content, "utf-8");
|
|
2051
2289
|
result.installed.push(filename);
|
|
2052
2290
|
}
|
|
2053
|
-
const ignoreDest =
|
|
2054
|
-
if (!await
|
|
2291
|
+
const ignoreDest = import_node_path2.default.join(targetPath, PRETTIER_IGNORE);
|
|
2292
|
+
if (!await import_fs_extra2.default.pathExists(ignoreDest) || options?.overwrite) {
|
|
2055
2293
|
const ignoreContent = generatePrettierIgnore();
|
|
2056
|
-
await
|
|
2294
|
+
await import_fs_extra2.default.writeFile(ignoreDest, ignoreContent, "utf-8");
|
|
2057
2295
|
result.installed.push(PRETTIER_IGNORE);
|
|
2058
2296
|
}
|
|
2059
2297
|
} catch (error) {
|
|
@@ -2063,14 +2301,14 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
2063
2301
|
}
|
|
2064
2302
|
if (config.commitlint) {
|
|
2065
2303
|
const filename = CODE_STYLE_FILES.commitlint;
|
|
2066
|
-
const destPath =
|
|
2304
|
+
const destPath = import_node_path2.default.join(targetPath, filename);
|
|
2067
2305
|
try {
|
|
2068
|
-
if (await
|
|
2306
|
+
if (await import_fs_extra2.default.pathExists(destPath) && !options?.overwrite) {
|
|
2069
2307
|
result.skipped.push(filename);
|
|
2070
2308
|
} else {
|
|
2071
2309
|
const commitlintOptions = config.commitlintOptions ?? DEFAULT_COMMITLINT_OPTIONS;
|
|
2072
2310
|
const content = generateCommitlintConfig(commitlintOptions);
|
|
2073
|
-
await
|
|
2311
|
+
await import_fs_extra2.default.writeFile(destPath, content, "utf-8");
|
|
2074
2312
|
result.installed.push(filename);
|
|
2075
2313
|
}
|
|
2076
2314
|
} catch (error) {
|
|
@@ -2141,6 +2379,214 @@ function showCodeStyleInstructions(config) {
|
|
|
2141
2379
|
}
|
|
2142
2380
|
}
|
|
2143
2381
|
|
|
2382
|
+
// src/lib/code-style/vscode-installer.ts
|
|
2383
|
+
init_cjs_shims();
|
|
2384
|
+
init_fs();
|
|
2385
|
+
function generateVSCodeSettings(config) {
|
|
2386
|
+
const settings = {};
|
|
2387
|
+
if (config.biome) {
|
|
2388
|
+
settings["editor.defaultFormatter"] = "biomejs.biome";
|
|
2389
|
+
settings["editor.formatOnSave"] = true;
|
|
2390
|
+
settings["[javascript]"] = {
|
|
2391
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2392
|
+
};
|
|
2393
|
+
settings["[javascriptreact]"] = {
|
|
2394
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2395
|
+
};
|
|
2396
|
+
settings["[typescript]"] = {
|
|
2397
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2398
|
+
};
|
|
2399
|
+
settings["[typescriptreact]"] = {
|
|
2400
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2401
|
+
};
|
|
2402
|
+
settings["[json]"] = {
|
|
2403
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2404
|
+
};
|
|
2405
|
+
settings["[jsonc]"] = {
|
|
2406
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2407
|
+
};
|
|
2408
|
+
settings["eslint.enable"] = false;
|
|
2409
|
+
settings["biome.enabled"] = true;
|
|
2410
|
+
settings["biome.lintOnSave"] = true;
|
|
2411
|
+
}
|
|
2412
|
+
if (config.prettier && !config.biome) {
|
|
2413
|
+
settings["editor.defaultFormatter"] = "esbenp.prettier-vscode";
|
|
2414
|
+
settings["editor.formatOnSave"] = true;
|
|
2415
|
+
settings["[javascript]"] = {
|
|
2416
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2417
|
+
};
|
|
2418
|
+
settings["[javascriptreact]"] = {
|
|
2419
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2420
|
+
};
|
|
2421
|
+
settings["[typescript]"] = {
|
|
2422
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2423
|
+
};
|
|
2424
|
+
settings["[typescriptreact]"] = {
|
|
2425
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2426
|
+
};
|
|
2427
|
+
settings["[json]"] = {
|
|
2428
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2429
|
+
};
|
|
2430
|
+
settings["[markdown]"] = {
|
|
2431
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
if (config.editorconfig) {
|
|
2435
|
+
settings["editor.detectIndentation"] = false;
|
|
2436
|
+
}
|
|
2437
|
+
return settings;
|
|
2438
|
+
}
|
|
2439
|
+
function generateVSCodeExtensions(config) {
|
|
2440
|
+
const recommendations = [];
|
|
2441
|
+
if (config.biome) {
|
|
2442
|
+
recommendations.push("biomejs.biome");
|
|
2443
|
+
}
|
|
2444
|
+
if (config.prettier && !config.biome) {
|
|
2445
|
+
recommendations.push("esbenp.prettier-vscode");
|
|
2446
|
+
}
|
|
2447
|
+
if (config.editorconfig) {
|
|
2448
|
+
recommendations.push("EditorConfig.EditorConfig");
|
|
2449
|
+
}
|
|
2450
|
+
recommendations.push("dbaeumer.vscode-eslint");
|
|
2451
|
+
return { recommendations };
|
|
2452
|
+
}
|
|
2453
|
+
async function installVSCodeSettings(projectPath, config, options) {
|
|
2454
|
+
if (!config.enabled) {
|
|
2455
|
+
return {
|
|
2456
|
+
created: false,
|
|
2457
|
+
updated: false,
|
|
2458
|
+
skipped: true,
|
|
2459
|
+
path: ""
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
const vscodeDir = joinPath(projectPath, ".vscode");
|
|
2463
|
+
const settingsPath = joinPath(vscodeDir, "settings.json");
|
|
2464
|
+
try {
|
|
2465
|
+
const newSettings = generateVSCodeSettings(config);
|
|
2466
|
+
const exists = await pathExists(settingsPath);
|
|
2467
|
+
if (exists) {
|
|
2468
|
+
if (options?.merge) {
|
|
2469
|
+
const existingContent = await readFile(settingsPath);
|
|
2470
|
+
let existingSettings = {};
|
|
2471
|
+
try {
|
|
2472
|
+
existingSettings = JSON.parse(existingContent);
|
|
2473
|
+
} catch {
|
|
2474
|
+
logger.warn("Could not parse existing settings.json, will overwrite");
|
|
2475
|
+
}
|
|
2476
|
+
const mergedSettings = { ...existingSettings, ...newSettings };
|
|
2477
|
+
await writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
|
|
2478
|
+
return {
|
|
2479
|
+
created: false,
|
|
2480
|
+
updated: true,
|
|
2481
|
+
skipped: false,
|
|
2482
|
+
path: settingsPath
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
if (!options?.overwrite) {
|
|
2486
|
+
return {
|
|
2487
|
+
created: false,
|
|
2488
|
+
updated: false,
|
|
2489
|
+
skipped: true,
|
|
2490
|
+
path: settingsPath
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
await ensureDir(vscodeDir);
|
|
2495
|
+
await writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
2496
|
+
return {
|
|
2497
|
+
created: !exists,
|
|
2498
|
+
updated: exists,
|
|
2499
|
+
skipped: false,
|
|
2500
|
+
path: settingsPath
|
|
2501
|
+
};
|
|
2502
|
+
} catch (error) {
|
|
2503
|
+
return {
|
|
2504
|
+
created: false,
|
|
2505
|
+
updated: false,
|
|
2506
|
+
skipped: false,
|
|
2507
|
+
path: settingsPath,
|
|
2508
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
async function installVSCodeExtensions(projectPath, config, options) {
|
|
2513
|
+
if (!config.enabled) {
|
|
2514
|
+
return {
|
|
2515
|
+
created: false,
|
|
2516
|
+
updated: false,
|
|
2517
|
+
skipped: true,
|
|
2518
|
+
path: ""
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
const vscodeDir = joinPath(projectPath, ".vscode");
|
|
2522
|
+
const extensionsPath = joinPath(vscodeDir, "extensions.json");
|
|
2523
|
+
try {
|
|
2524
|
+
const newExtensions = generateVSCodeExtensions(config);
|
|
2525
|
+
const exists = await pathExists(extensionsPath);
|
|
2526
|
+
if (exists) {
|
|
2527
|
+
if (options?.merge) {
|
|
2528
|
+
const existingContent = await readFile(extensionsPath);
|
|
2529
|
+
let existingExtensions = { recommendations: [] };
|
|
2530
|
+
try {
|
|
2531
|
+
existingExtensions = JSON.parse(existingContent);
|
|
2532
|
+
} catch {
|
|
2533
|
+
logger.warn("Could not parse existing extensions.json, will overwrite");
|
|
2534
|
+
}
|
|
2535
|
+
const mergedRecommendations = [
|
|
2536
|
+
.../* @__PURE__ */ new Set([
|
|
2537
|
+
...existingExtensions.recommendations || [],
|
|
2538
|
+
...newExtensions.recommendations
|
|
2539
|
+
])
|
|
2540
|
+
];
|
|
2541
|
+
await writeFile(
|
|
2542
|
+
extensionsPath,
|
|
2543
|
+
JSON.stringify({ recommendations: mergedRecommendations }, null, 2)
|
|
2544
|
+
);
|
|
2545
|
+
return {
|
|
2546
|
+
created: false,
|
|
2547
|
+
updated: true,
|
|
2548
|
+
skipped: false,
|
|
2549
|
+
path: extensionsPath
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
if (!options?.overwrite) {
|
|
2553
|
+
return {
|
|
2554
|
+
created: false,
|
|
2555
|
+
updated: false,
|
|
2556
|
+
skipped: true,
|
|
2557
|
+
path: extensionsPath
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
await ensureDir(vscodeDir);
|
|
2562
|
+
await writeFile(extensionsPath, JSON.stringify(newExtensions, null, 2));
|
|
2563
|
+
return {
|
|
2564
|
+
created: !exists,
|
|
2565
|
+
updated: exists,
|
|
2566
|
+
skipped: false,
|
|
2567
|
+
path: extensionsPath
|
|
2568
|
+
};
|
|
2569
|
+
} catch (error) {
|
|
2570
|
+
return {
|
|
2571
|
+
created: false,
|
|
2572
|
+
updated: false,
|
|
2573
|
+
skipped: false,
|
|
2574
|
+
path: extensionsPath,
|
|
2575
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
async function installVSCodeConfig(projectPath, config, options) {
|
|
2580
|
+
const [settingsResult, extensionsResult] = await Promise.all([
|
|
2581
|
+
installVSCodeSettings(projectPath, config, options),
|
|
2582
|
+
installVSCodeExtensions(projectPath, config, options)
|
|
2583
|
+
]);
|
|
2584
|
+
return {
|
|
2585
|
+
settings: settingsResult,
|
|
2586
|
+
extensions: extensionsResult
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2144
2590
|
// src/lib/config/index.ts
|
|
2145
2591
|
init_cjs_shims();
|
|
2146
2592
|
|
|
@@ -2802,7 +3248,172 @@ function formatManualInstallInstructions(report) {
|
|
|
2802
3248
|
lines.push("");
|
|
2803
3249
|
}
|
|
2804
3250
|
}
|
|
2805
|
-
return lines;
|
|
3251
|
+
return lines;
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
// src/lib/git-hooks/index.ts
|
|
3255
|
+
init_cjs_shims();
|
|
3256
|
+
|
|
3257
|
+
// src/lib/git-hooks/husky-installer.ts
|
|
3258
|
+
init_cjs_shims();
|
|
3259
|
+
init_fs();
|
|
3260
|
+
function generateCommitMsgHook() {
|
|
3261
|
+
return `#!/usr/bin/env sh
|
|
3262
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3263
|
+
|
|
3264
|
+
npx --no -- commitlint --edit "\${1}"
|
|
3265
|
+
`;
|
|
3266
|
+
}
|
|
3267
|
+
function generatePreCommitHook(lintCommand) {
|
|
3268
|
+
const command = lintCommand || "pnpm lint-staged";
|
|
3269
|
+
return `#!/usr/bin/env sh
|
|
3270
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3271
|
+
|
|
3272
|
+
${command}
|
|
3273
|
+
`;
|
|
3274
|
+
}
|
|
3275
|
+
function generatePrePushHook(testCommand) {
|
|
3276
|
+
const command = testCommand || "pnpm test";
|
|
3277
|
+
return `#!/usr/bin/env sh
|
|
3278
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3279
|
+
|
|
3280
|
+
${command}
|
|
3281
|
+
`;
|
|
3282
|
+
}
|
|
3283
|
+
function generateHuskyScript() {
|
|
3284
|
+
return `#!/usr/bin/env sh
|
|
3285
|
+
if [ -z "$husky_skip_init" ]; then
|
|
3286
|
+
debug () {
|
|
3287
|
+
if [ "$HUSKY_DEBUG" = "1" ]; then
|
|
3288
|
+
echo "husky (debug) - $1"
|
|
3289
|
+
fi
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
readonly hook_name="$(basename -- "$0")"
|
|
3293
|
+
debug "starting $hook_name..."
|
|
3294
|
+
|
|
3295
|
+
if [ "$HUSKY" = "0" ]; then
|
|
3296
|
+
debug "HUSKY env variable is set to 0, skipping hook"
|
|
3297
|
+
exit 0
|
|
3298
|
+
fi
|
|
3299
|
+
|
|
3300
|
+
if [ -f ~/.huskyrc ]; then
|
|
3301
|
+
debug "sourcing ~/.huskyrc"
|
|
3302
|
+
. ~/.huskyrc
|
|
3303
|
+
fi
|
|
3304
|
+
|
|
3305
|
+
readonly husky_skip_init=1
|
|
3306
|
+
export husky_skip_init
|
|
3307
|
+
sh -e "$0" "$@"
|
|
3308
|
+
exitCode="$?"
|
|
3309
|
+
|
|
3310
|
+
if [ $exitCode != 0 ]; then
|
|
3311
|
+
echo "husky - $hook_name hook exited with code $exitCode (error)"
|
|
3312
|
+
fi
|
|
3313
|
+
|
|
3314
|
+
if [ $exitCode = 127 ]; then
|
|
3315
|
+
echo "husky - command not found in PATH=$PATH"
|
|
3316
|
+
fi
|
|
3317
|
+
|
|
3318
|
+
exit $exitCode
|
|
3319
|
+
fi
|
|
3320
|
+
`;
|
|
3321
|
+
}
|
|
3322
|
+
function generateHuskyGitignore() {
|
|
3323
|
+
return `_
|
|
3324
|
+
`;
|
|
3325
|
+
}
|
|
3326
|
+
async function installHusky(projectPath, config, options) {
|
|
3327
|
+
const result = {
|
|
3328
|
+
created: [],
|
|
3329
|
+
skipped: [],
|
|
3330
|
+
errors: [],
|
|
3331
|
+
initialized: false
|
|
3332
|
+
};
|
|
3333
|
+
const huskyDir = joinPath(projectPath, ".husky");
|
|
3334
|
+
const huskyInternalDir = joinPath(huskyDir, "_");
|
|
3335
|
+
try {
|
|
3336
|
+
await ensureDir(huskyDir);
|
|
3337
|
+
await ensureDir(huskyInternalDir);
|
|
3338
|
+
result.initialized = true;
|
|
3339
|
+
const huskyScriptPath = joinPath(huskyInternalDir, "husky.sh");
|
|
3340
|
+
if (!await pathExists(huskyScriptPath) || options?.overwrite) {
|
|
3341
|
+
await writeFile(huskyScriptPath, generateHuskyScript());
|
|
3342
|
+
await makeExecutable(huskyScriptPath);
|
|
3343
|
+
result.created.push("_/husky.sh");
|
|
3344
|
+
} else {
|
|
3345
|
+
result.skipped.push("_/husky.sh");
|
|
3346
|
+
}
|
|
3347
|
+
const gitignorePath = joinPath(huskyInternalDir, ".gitignore");
|
|
3348
|
+
if (!await pathExists(gitignorePath) || options?.overwrite) {
|
|
3349
|
+
await writeFile(gitignorePath, generateHuskyGitignore());
|
|
3350
|
+
result.created.push("_/.gitignore");
|
|
3351
|
+
}
|
|
3352
|
+
if (config.commitlint) {
|
|
3353
|
+
const commitMsgPath = joinPath(huskyDir, "commit-msg");
|
|
3354
|
+
if (!await pathExists(commitMsgPath) || options?.overwrite) {
|
|
3355
|
+
await writeFile(commitMsgPath, generateCommitMsgHook());
|
|
3356
|
+
await makeExecutable(commitMsgPath);
|
|
3357
|
+
result.created.push("commit-msg");
|
|
3358
|
+
} else {
|
|
3359
|
+
result.skipped.push("commit-msg");
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
if (config.preCommit) {
|
|
3363
|
+
const preCommitPath = joinPath(huskyDir, "pre-commit");
|
|
3364
|
+
if (!await pathExists(preCommitPath) || options?.overwrite) {
|
|
3365
|
+
await writeFile(preCommitPath, generatePreCommitHook(config.lintCommand));
|
|
3366
|
+
await makeExecutable(preCommitPath);
|
|
3367
|
+
result.created.push("pre-commit");
|
|
3368
|
+
} else {
|
|
3369
|
+
result.skipped.push("pre-commit");
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
if (config.prePush) {
|
|
3373
|
+
const prePushPath = joinPath(huskyDir, "pre-push");
|
|
3374
|
+
if (!await pathExists(prePushPath) || options?.overwrite) {
|
|
3375
|
+
await writeFile(prePushPath, generatePrePushHook(config.testCommand));
|
|
3376
|
+
await makeExecutable(prePushPath);
|
|
3377
|
+
result.created.push("pre-push");
|
|
3378
|
+
} else {
|
|
3379
|
+
result.skipped.push("pre-push");
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
} catch (error) {
|
|
3383
|
+
result.errors.push(error instanceof Error ? error.message : String(error));
|
|
3384
|
+
}
|
|
3385
|
+
return result;
|
|
3386
|
+
}
|
|
3387
|
+
async function installHuskyWithSpinner(projectPath, config, options) {
|
|
3388
|
+
return withSpinner(
|
|
3389
|
+
"Installing Husky hooks...",
|
|
3390
|
+
() => installHusky(projectPath, config, options),
|
|
3391
|
+
{
|
|
3392
|
+
successText: "Installed Husky hooks"
|
|
3393
|
+
}
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
function deriveHuskyConfigFromCodeStyle(codeStyle) {
|
|
3397
|
+
if (!codeStyle.enabled) {
|
|
3398
|
+
return null;
|
|
3399
|
+
}
|
|
3400
|
+
const commitlintEnabled = codeStyle.commitlint && codeStyle.commitlintOptions?.huskyIntegration;
|
|
3401
|
+
if (!commitlintEnabled) {
|
|
3402
|
+
return null;
|
|
3403
|
+
}
|
|
3404
|
+
let lintCommand;
|
|
3405
|
+
if (codeStyle.biome) {
|
|
3406
|
+
lintCommand = "pnpm biome check --staged";
|
|
3407
|
+
} else if (codeStyle.prettier) {
|
|
3408
|
+
lintCommand = "pnpm lint-staged";
|
|
3409
|
+
}
|
|
3410
|
+
return {
|
|
3411
|
+
commitlint: true,
|
|
3412
|
+
preCommit: !!lintCommand,
|
|
3413
|
+
lintCommand,
|
|
3414
|
+
prePush: false
|
|
3415
|
+
// Don't run tests on push by default
|
|
3416
|
+
};
|
|
2806
3417
|
}
|
|
2807
3418
|
|
|
2808
3419
|
// src/lib/hooks/index.ts
|
|
@@ -3008,44 +3619,72 @@ var os2 = __toESM(require("os"), 1);
|
|
|
3008
3619
|
init_cjs_shims();
|
|
3009
3620
|
var MCP_SERVERS = [
|
|
3010
3621
|
// ============================================
|
|
3011
|
-
// DOCUMENTATION
|
|
3622
|
+
// DOCUMENTATION & AI TOOLS
|
|
3012
3623
|
// ============================================
|
|
3013
3624
|
{
|
|
3014
3625
|
id: "context7",
|
|
3015
3626
|
name: "Context7",
|
|
3016
|
-
description: "
|
|
3017
|
-
package: "@
|
|
3627
|
+
description: "Up-to-date documentation lookup for libraries and frameworks",
|
|
3628
|
+
package: "@upstash/context7-mcp",
|
|
3018
3629
|
category: "documentation",
|
|
3019
|
-
requiresConfig: false
|
|
3630
|
+
requiresConfig: false,
|
|
3631
|
+
installInstructions: "API keys provide higher rate limits. Get one at https://context7.com/dashboard"
|
|
3632
|
+
},
|
|
3633
|
+
{
|
|
3634
|
+
id: "perplexity",
|
|
3635
|
+
name: "Perplexity",
|
|
3636
|
+
description: "Web search without leaving the MCP ecosystem via Sonar API",
|
|
3637
|
+
package: "@chatmcp/server-perplexity-ask",
|
|
3638
|
+
category: "search",
|
|
3639
|
+
requiresConfig: true,
|
|
3640
|
+
configFields: [
|
|
3641
|
+
{
|
|
3642
|
+
name: "apiKey",
|
|
3643
|
+
type: "string",
|
|
3644
|
+
required: true,
|
|
3645
|
+
description: "Perplexity/Sonar API Key",
|
|
3646
|
+
envVar: "PERPLEXITY_API_KEY"
|
|
3647
|
+
}
|
|
3648
|
+
],
|
|
3649
|
+
installInstructions: "Get API key at https://www.perplexity.ai/settings/api"
|
|
3650
|
+
},
|
|
3651
|
+
{
|
|
3652
|
+
id: "sequential-thinking",
|
|
3653
|
+
name: "Sequential Thinking",
|
|
3654
|
+
description: "Dynamic problem-solving through structured thinking process",
|
|
3655
|
+
package: "@modelcontextprotocol/server-sequential-thinking",
|
|
3656
|
+
category: "ai",
|
|
3657
|
+
requiresConfig: false,
|
|
3658
|
+
installInstructions: "Helps break down complex problems into manageable steps."
|
|
3020
3659
|
},
|
|
3021
3660
|
// ============================================
|
|
3022
3661
|
// TESTING & BROWSER AUTOMATION
|
|
3023
3662
|
// ============================================
|
|
3024
3663
|
{
|
|
3025
|
-
id: "
|
|
3026
|
-
name: "
|
|
3027
|
-
description: "
|
|
3028
|
-
package: "@
|
|
3664
|
+
id: "puppeteer",
|
|
3665
|
+
name: "Puppeteer",
|
|
3666
|
+
description: "Headless Chrome automation for testing and scraping",
|
|
3667
|
+
package: "@modelcontextprotocol/server-puppeteer",
|
|
3029
3668
|
category: "testing",
|
|
3030
|
-
requiresConfig: false
|
|
3031
|
-
installInstructions: "Requires Chrome/Chromium browser installed on the system."
|
|
3669
|
+
requiresConfig: false
|
|
3032
3670
|
},
|
|
3033
3671
|
{
|
|
3034
3672
|
id: "playwright",
|
|
3035
3673
|
name: "Playwright",
|
|
3036
|
-
description: "
|
|
3037
|
-
package: "@
|
|
3674
|
+
description: "Browser automation via accessibility snapshots, not screenshots",
|
|
3675
|
+
package: "@playwright/mcp",
|
|
3038
3676
|
category: "testing",
|
|
3039
3677
|
requiresConfig: false,
|
|
3040
|
-
installInstructions: "
|
|
3678
|
+
installInstructions: "Requires Node.js 18+. Supports Chrome, Firefox, WebKit."
|
|
3041
3679
|
},
|
|
3042
3680
|
{
|
|
3043
|
-
id: "
|
|
3044
|
-
name: "
|
|
3045
|
-
description: "
|
|
3046
|
-
package: "
|
|
3681
|
+
id: "chrome-devtools",
|
|
3682
|
+
name: "Chrome DevTools",
|
|
3683
|
+
description: "Control and inspect live Chrome browser with DevTools Protocol",
|
|
3684
|
+
package: "chrome-devtools-mcp",
|
|
3047
3685
|
category: "testing",
|
|
3048
|
-
requiresConfig: false
|
|
3686
|
+
requiresConfig: false,
|
|
3687
|
+
installInstructions: "Requires Chrome installed. For Claude Code: claude mcp add chrome-devtools npx chrome-devtools-mcp@latest"
|
|
3049
3688
|
},
|
|
3050
3689
|
// ============================================
|
|
3051
3690
|
// VERSION CONTROL
|
|
@@ -3053,7 +3692,7 @@ var MCP_SERVERS = [
|
|
|
3053
3692
|
{
|
|
3054
3693
|
id: "github",
|
|
3055
3694
|
name: "GitHub",
|
|
3056
|
-
description: "GitHub API integration (issues, PRs, repos)",
|
|
3695
|
+
description: "GitHub API integration (issues, PRs, repos, file operations)",
|
|
3057
3696
|
package: "@modelcontextprotocol/server-github",
|
|
3058
3697
|
category: "version-control",
|
|
3059
3698
|
requiresConfig: true,
|
|
@@ -3066,32 +3705,33 @@ var MCP_SERVERS = [
|
|
|
3066
3705
|
envVar: "GITHUB_TOKEN"
|
|
3067
3706
|
}
|
|
3068
3707
|
],
|
|
3069
|
-
installInstructions: "Create a Personal Access Token at https://github.com/settings/tokens with repo
|
|
3708
|
+
installInstructions: "Create a Personal Access Token at https://github.com/settings/tokens with repo scope."
|
|
3070
3709
|
},
|
|
3071
3710
|
{
|
|
3072
3711
|
id: "gitlab",
|
|
3073
3712
|
name: "GitLab",
|
|
3074
|
-
description: "GitLab API
|
|
3075
|
-
package: "@
|
|
3713
|
+
description: "GitLab API for project management, issues, and merge requests",
|
|
3714
|
+
package: "@modelcontextprotocol/server-gitlab",
|
|
3076
3715
|
category: "version-control",
|
|
3077
3716
|
requiresConfig: true,
|
|
3078
3717
|
configFields: [
|
|
3079
3718
|
{
|
|
3080
3719
|
name: "token",
|
|
3081
3720
|
type: "string",
|
|
3082
|
-
required:
|
|
3721
|
+
required: true,
|
|
3083
3722
|
description: "GitLab Personal Access Token",
|
|
3084
|
-
envVar: "
|
|
3723
|
+
envVar: "GITLAB_PERSONAL_ACCESS_TOKEN"
|
|
3085
3724
|
},
|
|
3086
3725
|
{
|
|
3087
|
-
name: "
|
|
3726
|
+
name: "apiUrl",
|
|
3088
3727
|
type: "string",
|
|
3089
3728
|
required: false,
|
|
3090
|
-
description: "GitLab
|
|
3091
|
-
|
|
3729
|
+
description: "GitLab API URL (default: https://gitlab.com/api/v4)",
|
|
3730
|
+
envVar: "GITLAB_API_URL",
|
|
3731
|
+
default: "https://gitlab.com/api/v4"
|
|
3092
3732
|
}
|
|
3093
3733
|
],
|
|
3094
|
-
installInstructions: "Create
|
|
3734
|
+
installInstructions: "Create PAT at GitLab User Settings > Access Tokens with api, read_repository, write_repository scopes."
|
|
3095
3735
|
},
|
|
3096
3736
|
// ============================================
|
|
3097
3737
|
// DATABASES
|
|
@@ -3099,7 +3739,7 @@ var MCP_SERVERS = [
|
|
|
3099
3739
|
{
|
|
3100
3740
|
id: "postgres",
|
|
3101
3741
|
name: "PostgreSQL",
|
|
3102
|
-
description: "
|
|
3742
|
+
description: "Read-only PostgreSQL database access with schema inspection",
|
|
3103
3743
|
package: "@modelcontextprotocol/server-postgres",
|
|
3104
3744
|
category: "database",
|
|
3105
3745
|
requiresConfig: true,
|
|
@@ -3115,63 +3755,45 @@ var MCP_SERVERS = [
|
|
|
3115
3755
|
installInstructions: "Connection string format: postgresql://user:password@host:port/database"
|
|
3116
3756
|
},
|
|
3117
3757
|
{
|
|
3118
|
-
id: "
|
|
3119
|
-
name: "
|
|
3120
|
-
description: "
|
|
3121
|
-
package: "@
|
|
3122
|
-
category: "database",
|
|
3123
|
-
requiresConfig: true,
|
|
3124
|
-
configFields: [
|
|
3125
|
-
{
|
|
3126
|
-
name: "apiKey",
|
|
3127
|
-
type: "string",
|
|
3128
|
-
required: false,
|
|
3129
|
-
description: "Neon API Key",
|
|
3130
|
-
envVar: "NEON_API_KEY"
|
|
3131
|
-
}
|
|
3132
|
-
],
|
|
3133
|
-
installInstructions: "Get your API key from https://console.neon.tech/app/settings/api-keys"
|
|
3134
|
-
},
|
|
3135
|
-
{
|
|
3136
|
-
id: "mongodb",
|
|
3137
|
-
name: "MongoDB",
|
|
3138
|
-
description: "MongoDB document database access",
|
|
3139
|
-
package: "@anthropic/mongodb-mcp",
|
|
3758
|
+
id: "mysql",
|
|
3759
|
+
name: "MySQL",
|
|
3760
|
+
description: "Read-only MySQL/MariaDB database access",
|
|
3761
|
+
package: "@modelcontextprotocol/server-mysql",
|
|
3140
3762
|
category: "database",
|
|
3141
3763
|
requiresConfig: true,
|
|
3142
3764
|
configFields: [
|
|
3143
3765
|
{
|
|
3144
3766
|
name: "connectionString",
|
|
3145
3767
|
type: "string",
|
|
3146
|
-
required:
|
|
3147
|
-
description: "
|
|
3148
|
-
envVar: "
|
|
3768
|
+
required: true,
|
|
3769
|
+
description: "MySQL connection URL",
|
|
3770
|
+
envVar: "MYSQL_URL"
|
|
3149
3771
|
}
|
|
3150
3772
|
],
|
|
3151
|
-
installInstructions: "Connection
|
|
3773
|
+
installInstructions: "Connection format: mysql://user:password@host:port/database"
|
|
3152
3774
|
},
|
|
3153
3775
|
{
|
|
3154
|
-
id: "
|
|
3155
|
-
name: "
|
|
3156
|
-
description: "
|
|
3157
|
-
package: "@
|
|
3776
|
+
id: "neon",
|
|
3777
|
+
name: "Neon",
|
|
3778
|
+
description: "Neon serverless PostgreSQL with branch management",
|
|
3779
|
+
package: "@neondatabase/mcp-server-neon",
|
|
3158
3780
|
category: "database",
|
|
3159
3781
|
requiresConfig: true,
|
|
3160
3782
|
configFields: [
|
|
3161
3783
|
{
|
|
3162
|
-
name: "
|
|
3784
|
+
name: "apiKey",
|
|
3163
3785
|
type: "string",
|
|
3164
3786
|
required: false,
|
|
3165
|
-
description: "
|
|
3166
|
-
envVar: "
|
|
3787
|
+
description: "Neon API Key",
|
|
3788
|
+
envVar: "NEON_API_KEY"
|
|
3167
3789
|
}
|
|
3168
3790
|
],
|
|
3169
|
-
installInstructions: "
|
|
3791
|
+
installInstructions: "Get your API key from https://console.neon.tech/app/settings/api-keys"
|
|
3170
3792
|
},
|
|
3171
3793
|
{
|
|
3172
3794
|
id: "sqlite",
|
|
3173
3795
|
name: "SQLite",
|
|
3174
|
-
description: "SQLite
|
|
3796
|
+
description: "SQLite database interaction and business intelligence",
|
|
3175
3797
|
package: "@modelcontextprotocol/server-sqlite",
|
|
3176
3798
|
category: "database",
|
|
3177
3799
|
requiresConfig: true,
|
|
@@ -3188,27 +3810,20 @@ var MCP_SERVERS = [
|
|
|
3188
3810
|
{
|
|
3189
3811
|
id: "supabase",
|
|
3190
3812
|
name: "Supabase",
|
|
3191
|
-
description: "Supabase
|
|
3192
|
-
package: "@supabase/mcp-server",
|
|
3813
|
+
description: "Supabase projects, database, Edge Functions, and type generation",
|
|
3814
|
+
package: "@supabase/mcp-server-supabase",
|
|
3193
3815
|
category: "database",
|
|
3194
3816
|
requiresConfig: true,
|
|
3195
3817
|
configFields: [
|
|
3196
3818
|
{
|
|
3197
|
-
name: "
|
|
3198
|
-
type: "string",
|
|
3199
|
-
required: false,
|
|
3200
|
-
description: "Supabase project URL",
|
|
3201
|
-
envVar: "SUPABASE_URL"
|
|
3202
|
-
},
|
|
3203
|
-
{
|
|
3204
|
-
name: "anonKey",
|
|
3819
|
+
name: "accessToken",
|
|
3205
3820
|
type: "string",
|
|
3206
3821
|
required: false,
|
|
3207
|
-
description: "Supabase
|
|
3208
|
-
envVar: "
|
|
3822
|
+
description: "Supabase Personal Access Token",
|
|
3823
|
+
envVar: "SUPABASE_ACCESS_TOKEN"
|
|
3209
3824
|
}
|
|
3210
3825
|
],
|
|
3211
|
-
installInstructions: "
|
|
3826
|
+
installInstructions: "Get your access token from https://supabase.com/dashboard/account/tokens"
|
|
3212
3827
|
},
|
|
3213
3828
|
// ============================================
|
|
3214
3829
|
// CACHE & KEY-VALUE STORES
|
|
@@ -3216,8 +3831,8 @@ var MCP_SERVERS = [
|
|
|
3216
3831
|
{
|
|
3217
3832
|
id: "redis",
|
|
3218
3833
|
name: "Redis",
|
|
3219
|
-
description: "Redis
|
|
3220
|
-
package: "@
|
|
3834
|
+
description: "Redis key-value store operations (set, get, delete, list)",
|
|
3835
|
+
package: "@modelcontextprotocol/server-redis",
|
|
3221
3836
|
category: "cache",
|
|
3222
3837
|
requiresConfig: true,
|
|
3223
3838
|
configFields: [
|
|
@@ -3230,29 +3845,29 @@ var MCP_SERVERS = [
|
|
|
3230
3845
|
default: "redis://localhost:6379"
|
|
3231
3846
|
}
|
|
3232
3847
|
],
|
|
3233
|
-
installInstructions: "
|
|
3848
|
+
installInstructions: "Pass Redis URL as argument: npx @modelcontextprotocol/server-redis redis://localhost:6379"
|
|
3234
3849
|
},
|
|
3235
3850
|
{
|
|
3236
3851
|
id: "upstash",
|
|
3237
3852
|
name: "Upstash",
|
|
3238
|
-
description: "Upstash
|
|
3853
|
+
description: "Upstash Redis database management and commands",
|
|
3239
3854
|
package: "@upstash/mcp-server",
|
|
3240
3855
|
category: "cache",
|
|
3241
3856
|
requiresConfig: true,
|
|
3242
3857
|
configFields: [
|
|
3243
3858
|
{
|
|
3244
|
-
name: "
|
|
3859
|
+
name: "email",
|
|
3245
3860
|
type: "string",
|
|
3246
3861
|
required: false,
|
|
3247
|
-
description: "Upstash
|
|
3248
|
-
envVar: "
|
|
3862
|
+
description: "Upstash account email",
|
|
3863
|
+
envVar: "UPSTASH_EMAIL"
|
|
3249
3864
|
},
|
|
3250
3865
|
{
|
|
3251
|
-
name: "
|
|
3866
|
+
name: "apiKey",
|
|
3252
3867
|
type: "string",
|
|
3253
3868
|
required: false,
|
|
3254
|
-
description: "Upstash
|
|
3255
|
-
envVar: "
|
|
3869
|
+
description: "Upstash API Key",
|
|
3870
|
+
envVar: "UPSTASH_API_KEY"
|
|
3256
3871
|
}
|
|
3257
3872
|
],
|
|
3258
3873
|
installInstructions: "Get credentials from https://console.upstash.com"
|
|
@@ -3260,57 +3875,14 @@ var MCP_SERVERS = [
|
|
|
3260
3875
|
// ============================================
|
|
3261
3876
|
// DEPLOYMENT & INFRASTRUCTURE
|
|
3262
3877
|
// ============================================
|
|
3263
|
-
{
|
|
3264
|
-
id: "vercel",
|
|
3265
|
-
name: "Vercel",
|
|
3266
|
-
description: "Vercel deployment and project management",
|
|
3267
|
-
package: "@vercel/mcp",
|
|
3268
|
-
category: "deployment",
|
|
3269
|
-
requiresConfig: true,
|
|
3270
|
-
configFields: [
|
|
3271
|
-
{
|
|
3272
|
-
name: "token",
|
|
3273
|
-
type: "string",
|
|
3274
|
-
required: false,
|
|
3275
|
-
description: "Vercel Access Token",
|
|
3276
|
-
envVar: "VERCEL_TOKEN"
|
|
3277
|
-
}
|
|
3278
|
-
],
|
|
3279
|
-
installInstructions: "Create an access token at https://vercel.com/account/tokens"
|
|
3280
|
-
},
|
|
3281
|
-
{
|
|
3282
|
-
id: "netlify",
|
|
3283
|
-
name: "Netlify",
|
|
3284
|
-
description: "Netlify deployment and site management",
|
|
3285
|
-
package: "@anthropic/netlify-mcp",
|
|
3286
|
-
category: "deployment",
|
|
3287
|
-
requiresConfig: true,
|
|
3288
|
-
configFields: [
|
|
3289
|
-
{
|
|
3290
|
-
name: "token",
|
|
3291
|
-
type: "string",
|
|
3292
|
-
required: false,
|
|
3293
|
-
description: "Netlify Personal Access Token",
|
|
3294
|
-
envVar: "NETLIFY_TOKEN"
|
|
3295
|
-
}
|
|
3296
|
-
],
|
|
3297
|
-
installInstructions: "Create a token at https://app.netlify.com/user/applications#personal-access-tokens"
|
|
3298
|
-
},
|
|
3299
3878
|
{
|
|
3300
3879
|
id: "cloudflare",
|
|
3301
3880
|
name: "Cloudflare",
|
|
3302
|
-
description: "Cloudflare Workers,
|
|
3303
|
-
package: "@cloudflare/mcp-server",
|
|
3881
|
+
description: "Cloudflare Workers, D1, KV, R2, and DNS management",
|
|
3882
|
+
package: "@cloudflare/mcp-server-cloudflare",
|
|
3304
3883
|
category: "deployment",
|
|
3305
3884
|
requiresConfig: true,
|
|
3306
3885
|
configFields: [
|
|
3307
|
-
{
|
|
3308
|
-
name: "apiToken",
|
|
3309
|
-
type: "string",
|
|
3310
|
-
required: false,
|
|
3311
|
-
description: "Cloudflare API Token",
|
|
3312
|
-
envVar: "CLOUDFLARE_API_TOKEN"
|
|
3313
|
-
},
|
|
3314
3886
|
{
|
|
3315
3887
|
name: "accountId",
|
|
3316
3888
|
type: "string",
|
|
@@ -3319,170 +3891,89 @@ var MCP_SERVERS = [
|
|
|
3319
3891
|
envVar: "CLOUDFLARE_ACCOUNT_ID"
|
|
3320
3892
|
}
|
|
3321
3893
|
],
|
|
3322
|
-
installInstructions: "
|
|
3323
|
-
},
|
|
3324
|
-
{
|
|
3325
|
-
id: "aws",
|
|
3326
|
-
name: "AWS",
|
|
3327
|
-
description: "Amazon Web Services integration",
|
|
3328
|
-
package: "@anthropic/aws-mcp",
|
|
3329
|
-
category: "infrastructure",
|
|
3330
|
-
requiresConfig: true,
|
|
3331
|
-
configFields: [
|
|
3332
|
-
{
|
|
3333
|
-
name: "accessKeyId",
|
|
3334
|
-
type: "string",
|
|
3335
|
-
required: false,
|
|
3336
|
-
description: "AWS Access Key ID",
|
|
3337
|
-
envVar: "AWS_ACCESS_KEY_ID"
|
|
3338
|
-
},
|
|
3339
|
-
{
|
|
3340
|
-
name: "secretAccessKey",
|
|
3341
|
-
type: "string",
|
|
3342
|
-
required: false,
|
|
3343
|
-
description: "AWS Secret Access Key",
|
|
3344
|
-
envVar: "AWS_SECRET_ACCESS_KEY"
|
|
3345
|
-
},
|
|
3346
|
-
{
|
|
3347
|
-
name: "region",
|
|
3348
|
-
type: "string",
|
|
3349
|
-
required: false,
|
|
3350
|
-
description: "AWS Region",
|
|
3351
|
-
envVar: "AWS_REGION",
|
|
3352
|
-
default: "us-east-1"
|
|
3353
|
-
}
|
|
3354
|
-
],
|
|
3355
|
-
installInstructions: "Create credentials in AWS IAM Console with appropriate permissions."
|
|
3356
|
-
},
|
|
3357
|
-
{
|
|
3358
|
-
id: "docker",
|
|
3359
|
-
name: "Docker",
|
|
3360
|
-
description: "Docker container management",
|
|
3361
|
-
package: "@anthropic/docker-mcp",
|
|
3362
|
-
category: "infrastructure",
|
|
3363
|
-
requiresConfig: false,
|
|
3364
|
-
installInstructions: "Requires Docker Desktop or Docker Engine installed."
|
|
3894
|
+
installInstructions: "Run: npx @cloudflare/mcp-server-cloudflare init"
|
|
3365
3895
|
},
|
|
3366
3896
|
{
|
|
3367
|
-
id: "
|
|
3368
|
-
name: "
|
|
3369
|
-
description: "
|
|
3370
|
-
package: "
|
|
3371
|
-
category: "
|
|
3897
|
+
id: "vercel",
|
|
3898
|
+
name: "Vercel",
|
|
3899
|
+
description: "Vercel deployments, DNS records, and project management",
|
|
3900
|
+
package: "vercel-mcp",
|
|
3901
|
+
category: "deployment",
|
|
3372
3902
|
requiresConfig: true,
|
|
3373
3903
|
configFields: [
|
|
3374
3904
|
{
|
|
3375
|
-
name: "
|
|
3376
|
-
type: "string",
|
|
3377
|
-
required: false,
|
|
3378
|
-
description: "Path to kubeconfig file",
|
|
3379
|
-
default: "~/.kube/config"
|
|
3380
|
-
},
|
|
3381
|
-
{
|
|
3382
|
-
name: "context",
|
|
3905
|
+
name: "apiKey",
|
|
3383
3906
|
type: "string",
|
|
3384
|
-
required:
|
|
3385
|
-
description: "
|
|
3907
|
+
required: true,
|
|
3908
|
+
description: "Vercel API Key",
|
|
3909
|
+
envVar: "VERCEL_API_KEY"
|
|
3386
3910
|
}
|
|
3387
3911
|
],
|
|
3388
|
-
installInstructions: "
|
|
3912
|
+
installInstructions: "Get API key at https://vercel.com/account/tokens"
|
|
3389
3913
|
},
|
|
3390
3914
|
{
|
|
3391
3915
|
id: "filesystem",
|
|
3392
3916
|
name: "Filesystem",
|
|
3393
|
-
description: "
|
|
3917
|
+
description: "Secure file operations with configurable access controls",
|
|
3394
3918
|
package: "@modelcontextprotocol/server-filesystem",
|
|
3395
3919
|
category: "infrastructure",
|
|
3396
3920
|
requiresConfig: false
|
|
3397
3921
|
},
|
|
3398
|
-
// ============================================
|
|
3399
|
-
// PROJECT MANAGEMENT
|
|
3400
|
-
// ============================================
|
|
3401
|
-
{
|
|
3402
|
-
id: "linear",
|
|
3403
|
-
name: "Linear",
|
|
3404
|
-
description: "Linear project management integration",
|
|
3405
|
-
package: "@anthropic/linear-mcp",
|
|
3406
|
-
category: "project-mgmt",
|
|
3407
|
-
requiresConfig: true,
|
|
3408
|
-
configFields: [
|
|
3409
|
-
{
|
|
3410
|
-
name: "apiKey",
|
|
3411
|
-
type: "string",
|
|
3412
|
-
required: false,
|
|
3413
|
-
description: "Linear API Key",
|
|
3414
|
-
envVar: "LINEAR_API_KEY"
|
|
3415
|
-
}
|
|
3416
|
-
],
|
|
3417
|
-
installInstructions: "Create an API key at https://linear.app/settings/api"
|
|
3418
|
-
},
|
|
3419
3922
|
{
|
|
3420
|
-
id: "
|
|
3421
|
-
name: "
|
|
3422
|
-
description: "
|
|
3423
|
-
package: "@
|
|
3424
|
-
category: "
|
|
3425
|
-
requiresConfig:
|
|
3426
|
-
|
|
3427
|
-
{
|
|
3428
|
-
name: "host",
|
|
3429
|
-
type: "string",
|
|
3430
|
-
required: false,
|
|
3431
|
-
description: "Jira instance URL",
|
|
3432
|
-
envVar: "JIRA_HOST"
|
|
3433
|
-
},
|
|
3434
|
-
{
|
|
3435
|
-
name: "email",
|
|
3436
|
-
type: "string",
|
|
3437
|
-
required: false,
|
|
3438
|
-
description: "Jira account email",
|
|
3439
|
-
envVar: "JIRA_EMAIL"
|
|
3440
|
-
},
|
|
3441
|
-
{
|
|
3442
|
-
name: "token",
|
|
3443
|
-
type: "string",
|
|
3444
|
-
required: false,
|
|
3445
|
-
description: "Jira API Token",
|
|
3446
|
-
envVar: "JIRA_TOKEN"
|
|
3447
|
-
}
|
|
3448
|
-
],
|
|
3449
|
-
installInstructions: "Create an API token at https://id.atlassian.com/manage-profile/security/api-tokens"
|
|
3923
|
+
id: "memory",
|
|
3924
|
+
name: "Memory",
|
|
3925
|
+
description: "Knowledge graph-based persistent memory system",
|
|
3926
|
+
package: "@modelcontextprotocol/server-memory",
|
|
3927
|
+
category: "infrastructure",
|
|
3928
|
+
requiresConfig: false,
|
|
3929
|
+
installInstructions: "Memory is stored in memory.jsonl in the server directory by default."
|
|
3450
3930
|
},
|
|
3931
|
+
// ============================================
|
|
3932
|
+
// PROJECT MANAGEMENT & PRODUCTIVITY
|
|
3933
|
+
// ============================================
|
|
3451
3934
|
{
|
|
3452
3935
|
id: "notion",
|
|
3453
3936
|
name: "Notion",
|
|
3454
|
-
description: "Notion
|
|
3455
|
-
package: "@
|
|
3937
|
+
description: "Official Notion API for pages, databases, and workspace",
|
|
3938
|
+
package: "@notionhq/notion-mcp-server",
|
|
3456
3939
|
category: "project-mgmt",
|
|
3457
3940
|
requiresConfig: true,
|
|
3458
3941
|
configFields: [
|
|
3459
3942
|
{
|
|
3460
3943
|
name: "token",
|
|
3461
3944
|
type: "string",
|
|
3462
|
-
required:
|
|
3945
|
+
required: true,
|
|
3463
3946
|
description: "Notion Integration Token",
|
|
3464
3947
|
envVar: "NOTION_TOKEN"
|
|
3465
3948
|
}
|
|
3466
3949
|
],
|
|
3467
|
-
installInstructions: "Create
|
|
3950
|
+
installInstructions: "Create integration at https://www.notion.so/profile/integrations and share pages with it."
|
|
3468
3951
|
},
|
|
3469
3952
|
{
|
|
3470
|
-
id: "
|
|
3471
|
-
name: "
|
|
3472
|
-
description: "
|
|
3473
|
-
package: "
|
|
3953
|
+
id: "obsidian",
|
|
3954
|
+
name: "Obsidian",
|
|
3955
|
+
description: "Read and search Obsidian vaults and Markdown directories",
|
|
3956
|
+
package: "mcp-obsidian",
|
|
3474
3957
|
category: "project-mgmt",
|
|
3475
3958
|
requiresConfig: true,
|
|
3476
3959
|
configFields: [
|
|
3477
3960
|
{
|
|
3478
|
-
name: "
|
|
3961
|
+
name: "vaultPath",
|
|
3479
3962
|
type: "string",
|
|
3480
|
-
required:
|
|
3481
|
-
description: "
|
|
3482
|
-
envVar: "ASANA_TOKEN"
|
|
3963
|
+
required: true,
|
|
3964
|
+
description: "Path to Obsidian vault"
|
|
3483
3965
|
}
|
|
3484
3966
|
],
|
|
3485
|
-
installInstructions: "
|
|
3967
|
+
installInstructions: "Works with any Markdown directory. Point to your vault path."
|
|
3968
|
+
},
|
|
3969
|
+
{
|
|
3970
|
+
id: "n8n",
|
|
3971
|
+
name: "n8n",
|
|
3972
|
+
description: "n8n workflow automation node documentation and management",
|
|
3973
|
+
package: "n8n-mcp",
|
|
3974
|
+
category: "project-mgmt",
|
|
3975
|
+
requiresConfig: false,
|
|
3976
|
+
installInstructions: "Provides access to 543 n8n nodes documentation."
|
|
3486
3977
|
},
|
|
3487
3978
|
// ============================================
|
|
3488
3979
|
// MONITORING & OBSERVABILITY
|
|
@@ -3490,7 +3981,7 @@ var MCP_SERVERS = [
|
|
|
3490
3981
|
{
|
|
3491
3982
|
id: "sentry",
|
|
3492
3983
|
name: "Sentry",
|
|
3493
|
-
description: "
|
|
3984
|
+
description: "Query Sentry errors, issues, and project information",
|
|
3494
3985
|
package: "@sentry/mcp-server",
|
|
3495
3986
|
category: "monitoring",
|
|
3496
3987
|
requiresConfig: true,
|
|
@@ -3501,255 +3992,145 @@ var MCP_SERVERS = [
|
|
|
3501
3992
|
required: false,
|
|
3502
3993
|
description: "Sentry Auth Token",
|
|
3503
3994
|
envVar: "SENTRY_AUTH_TOKEN"
|
|
3504
|
-
},
|
|
3505
|
-
{
|
|
3506
|
-
name: "org",
|
|
3507
|
-
type: "string",
|
|
3508
|
-
required: false,
|
|
3509
|
-
description: "Sentry Organization slug"
|
|
3510
|
-
}
|
|
3511
|
-
],
|
|
3512
|
-
installInstructions: "Create an auth token at https://sentry.io/settings/account/api/auth-tokens/"
|
|
3513
|
-
},
|
|
3514
|
-
{
|
|
3515
|
-
id: "datadog",
|
|
3516
|
-
name: "Datadog",
|
|
3517
|
-
description: "Datadog monitoring and APM",
|
|
3518
|
-
package: "@anthropic/datadog-mcp",
|
|
3519
|
-
category: "monitoring",
|
|
3520
|
-
requiresConfig: true,
|
|
3521
|
-
configFields: [
|
|
3522
|
-
{
|
|
3523
|
-
name: "apiKey",
|
|
3524
|
-
type: "string",
|
|
3525
|
-
required: false,
|
|
3526
|
-
description: "Datadog API Key",
|
|
3527
|
-
envVar: "DD_API_KEY"
|
|
3528
|
-
},
|
|
3529
|
-
{
|
|
3530
|
-
name: "appKey",
|
|
3531
|
-
type: "string",
|
|
3532
|
-
required: false,
|
|
3533
|
-
description: "Datadog Application Key",
|
|
3534
|
-
envVar: "DD_APP_KEY"
|
|
3535
|
-
},
|
|
3536
|
-
{
|
|
3537
|
-
name: "site",
|
|
3538
|
-
type: "string",
|
|
3539
|
-
required: false,
|
|
3540
|
-
description: "Datadog site (e.g., datadoghq.com)",
|
|
3541
|
-
default: "datadoghq.com"
|
|
3542
|
-
}
|
|
3543
|
-
],
|
|
3544
|
-
installInstructions: "Find keys at https://app.datadoghq.com/organization-settings/api-keys"
|
|
3545
|
-
},
|
|
3546
|
-
// ============================================
|
|
3547
|
-
// COMMUNICATION
|
|
3548
|
-
// ============================================
|
|
3549
|
-
{
|
|
3550
|
-
id: "slack",
|
|
3551
|
-
name: "Slack",
|
|
3552
|
-
description: "Slack messaging and channel management",
|
|
3553
|
-
package: "@anthropic/slack-mcp",
|
|
3554
|
-
category: "communication",
|
|
3555
|
-
requiresConfig: true,
|
|
3556
|
-
configFields: [
|
|
3557
|
-
{
|
|
3558
|
-
name: "token",
|
|
3559
|
-
type: "string",
|
|
3560
|
-
required: false,
|
|
3561
|
-
description: "Slack Bot Token (xoxb-...)",
|
|
3562
|
-
envVar: "SLACK_BOT_TOKEN"
|
|
3563
|
-
}
|
|
3564
|
-
],
|
|
3565
|
-
installInstructions: "Create a Slack app at https://api.slack.com/apps and install to your workspace."
|
|
3566
|
-
},
|
|
3567
|
-
{
|
|
3568
|
-
id: "discord",
|
|
3569
|
-
name: "Discord",
|
|
3570
|
-
description: "Discord bot and server management",
|
|
3571
|
-
package: "@anthropic/discord-mcp",
|
|
3572
|
-
category: "communication",
|
|
3573
|
-
requiresConfig: true,
|
|
3574
|
-
configFields: [
|
|
3575
|
-
{
|
|
3576
|
-
name: "token",
|
|
3577
|
-
type: "string",
|
|
3578
|
-
required: false,
|
|
3579
|
-
description: "Discord Bot Token",
|
|
3580
|
-
envVar: "DISCORD_BOT_TOKEN"
|
|
3581
|
-
}
|
|
3582
|
-
],
|
|
3583
|
-
installInstructions: "Create a bot at https://discord.com/developers/applications"
|
|
3584
|
-
},
|
|
3585
|
-
// ============================================
|
|
3586
|
-
// DESIGN
|
|
3587
|
-
// ============================================
|
|
3588
|
-
{
|
|
3589
|
-
id: "figma",
|
|
3590
|
-
name: "Figma",
|
|
3591
|
-
description: "Figma design file access and inspection",
|
|
3592
|
-
package: "@anthropic/figma-mcp",
|
|
3593
|
-
category: "design",
|
|
3594
|
-
requiresConfig: true,
|
|
3595
|
-
configFields: [
|
|
3596
|
-
{
|
|
3597
|
-
name: "token",
|
|
3598
|
-
type: "string",
|
|
3599
|
-
required: false,
|
|
3600
|
-
description: "Figma Personal Access Token",
|
|
3601
|
-
envVar: "FIGMA_TOKEN"
|
|
3602
3995
|
}
|
|
3603
3996
|
],
|
|
3604
|
-
installInstructions: "
|
|
3997
|
+
installInstructions: "For Claude Code: claude mcp add --transport http sentry https://mcp.sentry.dev/mcp"
|
|
3605
3998
|
},
|
|
3606
3999
|
// ============================================
|
|
3607
|
-
//
|
|
4000
|
+
// COMMUNICATION
|
|
3608
4001
|
// ============================================
|
|
3609
4002
|
{
|
|
3610
|
-
id: "
|
|
3611
|
-
name: "
|
|
3612
|
-
description: "
|
|
3613
|
-
package: "@
|
|
3614
|
-
category: "
|
|
4003
|
+
id: "slack",
|
|
4004
|
+
name: "Slack",
|
|
4005
|
+
description: "Slack messaging, channels, and workspace interaction",
|
|
4006
|
+
package: "@modelcontextprotocol/server-slack",
|
|
4007
|
+
category: "communication",
|
|
3615
4008
|
requiresConfig: true,
|
|
3616
4009
|
configFields: [
|
|
3617
4010
|
{
|
|
3618
|
-
name: "
|
|
4011
|
+
name: "token",
|
|
3619
4012
|
type: "string",
|
|
3620
4013
|
required: false,
|
|
3621
|
-
description: "
|
|
3622
|
-
envVar: "
|
|
4014
|
+
description: "Slack Bot Token (xoxb-...)",
|
|
4015
|
+
envVar: "SLACK_BOT_TOKEN"
|
|
3623
4016
|
}
|
|
3624
4017
|
],
|
|
3625
|
-
installInstructions: "
|
|
4018
|
+
installInstructions: "Create a Slack app at https://api.slack.com/apps and install to your workspace."
|
|
3626
4019
|
},
|
|
3627
4020
|
// ============================================
|
|
3628
|
-
//
|
|
4021
|
+
// DESIGN
|
|
3629
4022
|
// ============================================
|
|
3630
4023
|
{
|
|
3631
|
-
id: "
|
|
3632
|
-
name: "
|
|
3633
|
-
description: "
|
|
3634
|
-
package: "
|
|
3635
|
-
category: "
|
|
4024
|
+
id: "figma",
|
|
4025
|
+
name: "Figma",
|
|
4026
|
+
description: "Figma layout information for AI coding agents",
|
|
4027
|
+
package: "figma-developer-mcp",
|
|
4028
|
+
category: "design",
|
|
3636
4029
|
requiresConfig: true,
|
|
3637
4030
|
configFields: [
|
|
3638
|
-
{
|
|
3639
|
-
name: "appId",
|
|
3640
|
-
type: "string",
|
|
3641
|
-
required: false,
|
|
3642
|
-
description: "Algolia Application ID",
|
|
3643
|
-
envVar: "ALGOLIA_APP_ID"
|
|
3644
|
-
},
|
|
3645
4031
|
{
|
|
3646
4032
|
name: "apiKey",
|
|
3647
4033
|
type: "string",
|
|
3648
|
-
required:
|
|
3649
|
-
description: "
|
|
3650
|
-
envVar: "
|
|
4034
|
+
required: true,
|
|
4035
|
+
description: "Figma Personal Access Token",
|
|
4036
|
+
envVar: "FIGMA_API_KEY"
|
|
3651
4037
|
}
|
|
3652
4038
|
],
|
|
3653
|
-
installInstructions: "
|
|
4039
|
+
installInstructions: "Create token at https://www.figma.com/developers/api#access-tokens"
|
|
3654
4040
|
},
|
|
3655
4041
|
{
|
|
3656
|
-
id: "
|
|
3657
|
-
name: "
|
|
3658
|
-
description: "
|
|
3659
|
-
package: "@
|
|
3660
|
-
category: "
|
|
4042
|
+
id: "shadcn",
|
|
4043
|
+
name: "shadcn/ui",
|
|
4044
|
+
description: "shadcn/ui component docs, installation, and code generation",
|
|
4045
|
+
package: "@heilgar/shadcn-ui-mcp-server",
|
|
4046
|
+
category: "ui-library",
|
|
4047
|
+
requiresConfig: false,
|
|
4048
|
+
installInstructions: "Supports npm, pnpm, yarn, and bun package managers."
|
|
4049
|
+
},
|
|
4050
|
+
{
|
|
4051
|
+
id: "magic-ui",
|
|
4052
|
+
name: "21st.dev Magic",
|
|
4053
|
+
description: "AI-driven UI component generation through natural language",
|
|
4054
|
+
package: "@21st-dev/magic",
|
|
4055
|
+
category: "ui-library",
|
|
3661
4056
|
requiresConfig: true,
|
|
3662
4057
|
configFields: [
|
|
3663
|
-
{
|
|
3664
|
-
name: "node",
|
|
3665
|
-
type: "string",
|
|
3666
|
-
required: false,
|
|
3667
|
-
description: "Elasticsearch node URL",
|
|
3668
|
-
envVar: "ELASTICSEARCH_NODE",
|
|
3669
|
-
default: "http://localhost:9200"
|
|
3670
|
-
},
|
|
3671
4058
|
{
|
|
3672
4059
|
name: "apiKey",
|
|
3673
4060
|
type: "string",
|
|
3674
|
-
required:
|
|
3675
|
-
description: "
|
|
3676
|
-
envVar: "
|
|
4061
|
+
required: true,
|
|
4062
|
+
description: "21st.dev Magic API Key",
|
|
4063
|
+
envVar: "TWENTYFIRST_API_KEY"
|
|
3677
4064
|
}
|
|
3678
|
-
]
|
|
4065
|
+
],
|
|
4066
|
+
installInstructions: "Get API key at https://21st.dev/magic/console"
|
|
3679
4067
|
},
|
|
3680
4068
|
// ============================================
|
|
3681
|
-
//
|
|
4069
|
+
// PAYMENTS
|
|
3682
4070
|
// ============================================
|
|
3683
4071
|
{
|
|
3684
|
-
id: "
|
|
3685
|
-
name: "
|
|
3686
|
-
description: "
|
|
3687
|
-
package: "@
|
|
3688
|
-
category: "
|
|
4072
|
+
id: "stripe",
|
|
4073
|
+
name: "Stripe",
|
|
4074
|
+
description: "Stripe payments API with MCP support via Agent Toolkit",
|
|
4075
|
+
package: "@stripe/agent-toolkit",
|
|
4076
|
+
category: "payments",
|
|
3689
4077
|
requiresConfig: true,
|
|
3690
4078
|
configFields: [
|
|
3691
4079
|
{
|
|
3692
|
-
name: "
|
|
4080
|
+
name: "secretKey",
|
|
3693
4081
|
type: "string",
|
|
3694
4082
|
required: false,
|
|
3695
|
-
description: "
|
|
3696
|
-
envVar: "
|
|
4083
|
+
description: "Stripe Secret Key",
|
|
4084
|
+
envVar: "STRIPE_SECRET_KEY"
|
|
3697
4085
|
}
|
|
3698
4086
|
],
|
|
3699
|
-
installInstructions: "Get
|
|
4087
|
+
installInstructions: "Get API keys at https://dashboard.stripe.com/apikeys. Run: npx -y @stripe/mcp --tools=all --api-key=YOUR_KEY"
|
|
3700
4088
|
},
|
|
3701
4089
|
{
|
|
3702
|
-
id: "
|
|
3703
|
-
name: "
|
|
3704
|
-
description: "
|
|
3705
|
-
package: "
|
|
3706
|
-
category: "
|
|
4090
|
+
id: "mercadopago",
|
|
4091
|
+
name: "Mercado Pago",
|
|
4092
|
+
description: "Mercado Pago payments, refunds, and customer management",
|
|
4093
|
+
package: "mercado-pago-mcp",
|
|
4094
|
+
category: "payments",
|
|
3707
4095
|
requiresConfig: true,
|
|
3708
4096
|
configFields: [
|
|
3709
4097
|
{
|
|
3710
|
-
name: "
|
|
4098
|
+
name: "accessToken",
|
|
3711
4099
|
type: "string",
|
|
3712
|
-
required:
|
|
3713
|
-
description: "
|
|
3714
|
-
envVar: "
|
|
4100
|
+
required: true,
|
|
4101
|
+
description: "Mercado Pago Access Token",
|
|
4102
|
+
envVar: "MERCADOPAGO_ACCESS_TOKEN"
|
|
3715
4103
|
},
|
|
3716
4104
|
{
|
|
3717
4105
|
name: "environment",
|
|
3718
4106
|
type: "string",
|
|
3719
4107
|
required: false,
|
|
3720
|
-
description: "
|
|
3721
|
-
|
|
4108
|
+
description: "Environment (sandbox or production)",
|
|
4109
|
+
default: "sandbox"
|
|
3722
4110
|
}
|
|
3723
4111
|
],
|
|
3724
|
-
installInstructions: "Get credentials at https://
|
|
4112
|
+
installInstructions: "Get credentials at https://www.mercadopago.com/developers/panel/app"
|
|
3725
4113
|
},
|
|
3726
4114
|
// ============================================
|
|
3727
|
-
//
|
|
4115
|
+
// SEARCH
|
|
3728
4116
|
// ============================================
|
|
3729
4117
|
{
|
|
3730
|
-
id: "
|
|
3731
|
-
name: "
|
|
3732
|
-
description: "
|
|
3733
|
-
package: "@
|
|
3734
|
-
category: "
|
|
4118
|
+
id: "brave-search",
|
|
4119
|
+
name: "Brave Search",
|
|
4120
|
+
description: "Web and local search using Brave Search API",
|
|
4121
|
+
package: "@modelcontextprotocol/server-brave-search",
|
|
4122
|
+
category: "search",
|
|
3735
4123
|
requiresConfig: true,
|
|
3736
4124
|
configFields: [
|
|
3737
4125
|
{
|
|
3738
|
-
name: "
|
|
3739
|
-
type: "string",
|
|
3740
|
-
required: false,
|
|
3741
|
-
description: "Vault server address",
|
|
3742
|
-
envVar: "VAULT_ADDR",
|
|
3743
|
-
default: "http://127.0.0.1:8200"
|
|
3744
|
-
},
|
|
3745
|
-
{
|
|
3746
|
-
name: "token",
|
|
4126
|
+
name: "apiKey",
|
|
3747
4127
|
type: "string",
|
|
3748
4128
|
required: false,
|
|
3749
|
-
description: "
|
|
3750
|
-
envVar: "
|
|
4129
|
+
description: "Brave Search API Key",
|
|
4130
|
+
envVar: "BRAVE_API_KEY"
|
|
3751
4131
|
}
|
|
3752
|
-
]
|
|
4132
|
+
],
|
|
4133
|
+
installInstructions: "Get an API key at https://brave.com/search/api/"
|
|
3753
4134
|
}
|
|
3754
4135
|
];
|
|
3755
4136
|
function getMcpServer(id) {
|
|
@@ -4087,6 +4468,29 @@ async function installModules(category, modules, options) {
|
|
|
4087
4468
|
}
|
|
4088
4469
|
return result;
|
|
4089
4470
|
}
|
|
4471
|
+
async function installCategoryReadme(category, options) {
|
|
4472
|
+
const sourceReadme = joinPath(options.templatesPath, category, "README.md");
|
|
4473
|
+
const targetReadme = joinPath(options.targetPath, ".claude", category, "README.md");
|
|
4474
|
+
if (!await pathExists(sourceReadme)) {
|
|
4475
|
+
logger.debug(`No README found for category: ${category}`);
|
|
4476
|
+
return;
|
|
4477
|
+
}
|
|
4478
|
+
const targetExists = await pathExists(targetReadme);
|
|
4479
|
+
if (targetExists && !options.overwrite) {
|
|
4480
|
+
logger.debug(`README already exists for ${category}, skipping`);
|
|
4481
|
+
return;
|
|
4482
|
+
}
|
|
4483
|
+
if (options.dryRun) {
|
|
4484
|
+
logger.debug(`Would install README for ${category}`);
|
|
4485
|
+
return;
|
|
4486
|
+
}
|
|
4487
|
+
try {
|
|
4488
|
+
await copy(sourceReadme, targetReadme, { overwrite: options.overwrite });
|
|
4489
|
+
logger.debug(`Installed README for ${category}`);
|
|
4490
|
+
} catch (error) {
|
|
4491
|
+
logger.debug(`Failed to install README for ${category}: ${error}`);
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4090
4494
|
async function installAllModules(modulesByCategory, options) {
|
|
4091
4495
|
const results = {};
|
|
4092
4496
|
const categories = ["agents", "skills", "commands", "docs"];
|
|
@@ -4109,6 +4513,7 @@ async function installAllModules(modulesByCategory, options) {
|
|
|
4109
4513
|
silent: options.dryRun
|
|
4110
4514
|
}
|
|
4111
4515
|
);
|
|
4516
|
+
await installCategoryReadme(category, options);
|
|
4112
4517
|
}
|
|
4113
4518
|
return results;
|
|
4114
4519
|
}
|
|
@@ -4619,7 +5024,7 @@ async function updatePackageJson(projectPath, changes, options = {}) {
|
|
|
4619
5024
|
return result;
|
|
4620
5025
|
}
|
|
4621
5026
|
}
|
|
4622
|
-
function
|
|
5027
|
+
function getInstallCommand2(packageManager) {
|
|
4623
5028
|
switch (packageManager) {
|
|
4624
5029
|
case "npm":
|
|
4625
5030
|
return "npm install";
|
|
@@ -5324,6 +5729,218 @@ function showReplacementReport(report) {
|
|
|
5324
5729
|
}
|
|
5325
5730
|
}
|
|
5326
5731
|
|
|
5732
|
+
// src/lib/scaffold/claude-md-generator.ts
|
|
5733
|
+
init_cjs_shims();
|
|
5734
|
+
init_fs();
|
|
5735
|
+
|
|
5736
|
+
// src/lib/utils/paths.ts
|
|
5737
|
+
init_cjs_shims();
|
|
5738
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
5739
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
5740
|
+
var import_node_url = require("url");
|
|
5741
|
+
function getPackageRoot() {
|
|
5742
|
+
const currentFilePath = (0, import_node_url.fileURLToPath)(importMetaUrl);
|
|
5743
|
+
let currentDir = import_node_path3.default.dirname(currentFilePath);
|
|
5744
|
+
while (currentDir !== import_node_path3.default.dirname(currentDir)) {
|
|
5745
|
+
const packageJsonPath = import_node_path3.default.join(currentDir, "package.json");
|
|
5746
|
+
if (import_node_fs.default.existsSync(packageJsonPath)) {
|
|
5747
|
+
return currentDir;
|
|
5748
|
+
}
|
|
5749
|
+
currentDir = import_node_path3.default.dirname(currentDir);
|
|
5750
|
+
}
|
|
5751
|
+
throw new Error("Could not find package root (no package.json found in parent directories)");
|
|
5752
|
+
}
|
|
5753
|
+
function getTemplatesPath() {
|
|
5754
|
+
return import_node_path3.default.join(getPackageRoot(), "templates");
|
|
5755
|
+
}
|
|
5756
|
+
|
|
5757
|
+
// src/lib/scaffold/claude-md-generator.ts
|
|
5758
|
+
async function generateClaudeMd(projectPath, projectInfo, options) {
|
|
5759
|
+
const claudeMdPath = joinPath(projectPath, "CLAUDE.md");
|
|
5760
|
+
const exists = await pathExists(claudeMdPath);
|
|
5761
|
+
if (exists && !options?.overwrite) {
|
|
5762
|
+
return {
|
|
5763
|
+
created: false,
|
|
5764
|
+
skipped: true,
|
|
5765
|
+
path: claudeMdPath
|
|
5766
|
+
};
|
|
5767
|
+
}
|
|
5768
|
+
try {
|
|
5769
|
+
let template;
|
|
5770
|
+
if (options?.customTemplate) {
|
|
5771
|
+
template = options.customTemplate;
|
|
5772
|
+
} else {
|
|
5773
|
+
const templatePath = joinPath(getTemplatesPath(), "CLAUDE.md.template");
|
|
5774
|
+
if (await pathExists(templatePath)) {
|
|
5775
|
+
template = await readFile(templatePath);
|
|
5776
|
+
} else {
|
|
5777
|
+
template = getMinimalTemplate();
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
const content = processTemplate(template, projectInfo, options);
|
|
5781
|
+
await writeFile(claudeMdPath, content);
|
|
5782
|
+
return {
|
|
5783
|
+
created: true,
|
|
5784
|
+
skipped: false,
|
|
5785
|
+
path: claudeMdPath
|
|
5786
|
+
};
|
|
5787
|
+
} catch (error) {
|
|
5788
|
+
return {
|
|
5789
|
+
created: false,
|
|
5790
|
+
skipped: false,
|
|
5791
|
+
path: claudeMdPath,
|
|
5792
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5793
|
+
};
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
async function generateClaudeMdWithSpinner(projectPath, projectInfo, options) {
|
|
5797
|
+
return withSpinner(
|
|
5798
|
+
"Generating CLAUDE.md...",
|
|
5799
|
+
() => generateClaudeMd(projectPath, projectInfo, options),
|
|
5800
|
+
{
|
|
5801
|
+
successText: "Created CLAUDE.md"
|
|
5802
|
+
}
|
|
5803
|
+
);
|
|
5804
|
+
}
|
|
5805
|
+
function processTemplate(template, projectInfo, options) {
|
|
5806
|
+
let content = template;
|
|
5807
|
+
const techStack = options?.templateConfig?.techStack;
|
|
5808
|
+
const commands = options?.templateConfig?.commands;
|
|
5809
|
+
const targets = options?.templateConfig?.targets;
|
|
5810
|
+
const preferences = options?.claudeConfig?.preferences;
|
|
5811
|
+
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectInfo.name).replace(/\{\{PROJECT_DESCRIPTION\}\}/g, projectInfo.description).replace(/\{\{ORG\}\}/g, projectInfo.org).replace(/\{\{REPO\}\}/g, projectInfo.repo).replace(/\{\{ENTITY_TYPE\}\}/g, projectInfo.entityType).replace(/\{\{ENTITY_TYPE_PLURAL\}\}/g, projectInfo.entityTypePlural).replace(/\{\{LOCATION\}\}/g, projectInfo.location || "");
|
|
5812
|
+
const packageManager = preferences?.packageManager || "pnpm";
|
|
5813
|
+
content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
|
|
5814
|
+
const coverageTarget = targets && "coverage" in targets ? String(targets.coverage) : "90";
|
|
5815
|
+
content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
|
|
5816
|
+
if (projectInfo.domain) {
|
|
5817
|
+
content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
|
|
5818
|
+
} else {
|
|
5819
|
+
content = content.replace(/\{\{#if DOMAIN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
5820
|
+
}
|
|
5821
|
+
if (techStack && Object.keys(techStack).length > 0) {
|
|
5822
|
+
const techStackContent = generateTechStackSection(techStack);
|
|
5823
|
+
content = content.replace(/\{\{#if TECH_STACK\}\}/g, "").replace(/\{\{TECH_STACK\}\}\n\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, techStackContent);
|
|
5824
|
+
} else {
|
|
5825
|
+
content = content.replace(/\{\{#if TECH_STACK\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
5826
|
+
}
|
|
5827
|
+
if (commands && Object.keys(commands).length > 0) {
|
|
5828
|
+
const commandsContent = generateCommandsSection(commands, packageManager);
|
|
5829
|
+
content = content.replace(/\{\{#if COMMANDS\}\}/g, "").replace(/\{\{COMMANDS\}\}\n\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, commandsContent);
|
|
5830
|
+
} else {
|
|
5831
|
+
content = content.replace(/\{\{#if COMMANDS\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
5832
|
+
}
|
|
5833
|
+
content = content.replace(/\{\{#if PROJECT_STRUCTURE\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
5834
|
+
return content;
|
|
5835
|
+
}
|
|
5836
|
+
function generateTechStackSection(techStack) {
|
|
5837
|
+
const lines = [];
|
|
5838
|
+
if (techStack.frontendFramework && techStack.frontendFramework !== "None") {
|
|
5839
|
+
lines.push("**Frontend:**");
|
|
5840
|
+
lines.push(`- Framework: ${techStack.frontendFramework}`);
|
|
5841
|
+
if (techStack.stateManagement && techStack.stateManagement !== "None") {
|
|
5842
|
+
lines.push(`- State: ${techStack.stateManagement}`);
|
|
5843
|
+
}
|
|
5844
|
+
lines.push("");
|
|
5845
|
+
}
|
|
5846
|
+
if (techStack.apiFramework && techStack.apiFramework !== "None") {
|
|
5847
|
+
lines.push("**Backend:**");
|
|
5848
|
+
lines.push(`- API: ${techStack.apiFramework}`);
|
|
5849
|
+
if (techStack.validationLibrary && techStack.validationLibrary !== "None") {
|
|
5850
|
+
lines.push(`- Validation: ${techStack.validationLibrary}`);
|
|
5851
|
+
}
|
|
5852
|
+
lines.push("");
|
|
5853
|
+
}
|
|
5854
|
+
if (techStack.databaseOrm && techStack.databaseOrm !== "None") {
|
|
5855
|
+
lines.push("**Database:**");
|
|
5856
|
+
lines.push(`- ORM: ${techStack.databaseOrm}`);
|
|
5857
|
+
lines.push("");
|
|
5858
|
+
}
|
|
5859
|
+
if (techStack.authPattern && techStack.authPattern !== "None") {
|
|
5860
|
+
lines.push("**Authentication:**");
|
|
5861
|
+
lines.push(`- Provider: ${techStack.authPattern}`);
|
|
5862
|
+
lines.push("");
|
|
5863
|
+
}
|
|
5864
|
+
if (techStack.testFramework && techStack.testFramework !== "None") {
|
|
5865
|
+
lines.push("**Testing:**");
|
|
5866
|
+
lines.push(`- Framework: ${techStack.testFramework}`);
|
|
5867
|
+
lines.push("");
|
|
5868
|
+
}
|
|
5869
|
+
if (techStack.bundler && techStack.bundler !== "None") {
|
|
5870
|
+
lines.push("**Build:**");
|
|
5871
|
+
lines.push(`- Bundler: ${techStack.bundler}`);
|
|
5872
|
+
lines.push("");
|
|
5873
|
+
}
|
|
5874
|
+
return lines.join("\n");
|
|
5875
|
+
}
|
|
5876
|
+
function generateCommandsSection(commands, packageManager) {
|
|
5877
|
+
const lines = ["```bash"];
|
|
5878
|
+
lines.push("# Development");
|
|
5879
|
+
lines.push(`${packageManager} dev # Start development server`);
|
|
5880
|
+
lines.push("");
|
|
5881
|
+
lines.push("# Testing");
|
|
5882
|
+
if (commands.test) {
|
|
5883
|
+
lines.push(`${commands.test} # Run tests`);
|
|
5884
|
+
} else {
|
|
5885
|
+
lines.push(`${packageManager} test # Run tests`);
|
|
5886
|
+
}
|
|
5887
|
+
if (commands.coverage) {
|
|
5888
|
+
lines.push(`${commands.coverage} # Run tests with coverage`);
|
|
5889
|
+
} else {
|
|
5890
|
+
lines.push(`${packageManager} test:coverage # Run tests with coverage`);
|
|
5891
|
+
}
|
|
5892
|
+
lines.push("");
|
|
5893
|
+
lines.push("# Quality");
|
|
5894
|
+
if (commands.lint) {
|
|
5895
|
+
lines.push(`${commands.lint} # Run linter`);
|
|
5896
|
+
} else {
|
|
5897
|
+
lines.push(`${packageManager} lint # Run linter`);
|
|
5898
|
+
}
|
|
5899
|
+
if (commands.typecheck) {
|
|
5900
|
+
lines.push(`${commands.typecheck} # Type checking`);
|
|
5901
|
+
} else {
|
|
5902
|
+
lines.push(`${packageManager} typecheck # Type checking`);
|
|
5903
|
+
}
|
|
5904
|
+
if (commands.build) {
|
|
5905
|
+
lines.push("");
|
|
5906
|
+
lines.push("# Build");
|
|
5907
|
+
lines.push(`${commands.build} # Build for production`);
|
|
5908
|
+
}
|
|
5909
|
+
lines.push("```");
|
|
5910
|
+
return lines.join("\n");
|
|
5911
|
+
}
|
|
5912
|
+
function getMinimalTemplate() {
|
|
5913
|
+
return `# CLAUDE.md
|
|
5914
|
+
|
|
5915
|
+
## Project Overview
|
|
5916
|
+
|
|
5917
|
+
**{{PROJECT_NAME}}** - {{PROJECT_DESCRIPTION}}
|
|
5918
|
+
|
|
5919
|
+
## Repository
|
|
5920
|
+
|
|
5921
|
+
- **GitHub:** https://github.com/{{ORG}}/{{REPO}}
|
|
5922
|
+
|
|
5923
|
+
## Quick Commands
|
|
5924
|
+
|
|
5925
|
+
\`\`\`bash
|
|
5926
|
+
{{PACKAGE_MANAGER}} dev # Start development
|
|
5927
|
+
{{PACKAGE_MANAGER}} test # Run tests
|
|
5928
|
+
{{PACKAGE_MANAGER}} lint # Run linter
|
|
5929
|
+
{{PACKAGE_MANAGER}} build # Build project
|
|
5930
|
+
\`\`\`
|
|
5931
|
+
|
|
5932
|
+
## Claude Configuration
|
|
5933
|
+
|
|
5934
|
+
This project uses \`@qazuor/claude-code-config\` for AI-assisted development.
|
|
5935
|
+
|
|
5936
|
+
See \`.claude/docs/quick-start.md\` for getting started.
|
|
5937
|
+
|
|
5938
|
+
---
|
|
5939
|
+
|
|
5940
|
+
*Generated by [@qazuor/claude-code-config](https://github.com/qazuor/claude-code-config)*
|
|
5941
|
+
`;
|
|
5942
|
+
}
|
|
5943
|
+
|
|
5327
5944
|
// src/lib/scaffold/index.ts
|
|
5328
5945
|
init_cjs_shims();
|
|
5329
5946
|
|
|
@@ -5839,8 +6456,8 @@ async function generateScaffoldWithProgress(projectPath, options) {
|
|
|
5839
6456
|
|
|
5840
6457
|
// src/lib/templates/config-replacer.ts
|
|
5841
6458
|
init_cjs_shims();
|
|
5842
|
-
var
|
|
5843
|
-
var
|
|
6459
|
+
var fs4 = __toESM(require("fs/promises"), 1);
|
|
6460
|
+
var path5 = __toESM(require("path"), 1);
|
|
5844
6461
|
var import_ora2 = __toESM(require("ora"), 1);
|
|
5845
6462
|
|
|
5846
6463
|
// src/constants/template-placeholders.ts
|
|
@@ -6254,16 +6871,19 @@ var TEMPLATE_PLACEHOLDERS = [
|
|
|
6254
6871
|
choices: [
|
|
6255
6872
|
{ name: "React", value: "React" },
|
|
6256
6873
|
{ name: "Next.js", value: "Next.js" },
|
|
6874
|
+
{ name: "TanStack Start", value: "TanStack Start" },
|
|
6257
6875
|
{ name: "Vue", value: "Vue" },
|
|
6258
6876
|
{ name: "Nuxt", value: "Nuxt" },
|
|
6259
6877
|
{ name: "Svelte", value: "Svelte" },
|
|
6260
6878
|
{ name: "SvelteKit", value: "SvelteKit" },
|
|
6261
6879
|
{ name: "Astro", value: "Astro" },
|
|
6262
6880
|
{ name: "SolidJS", value: "SolidJS" },
|
|
6881
|
+
{ name: "Remix", value: "Remix" },
|
|
6263
6882
|
{ name: "Angular", value: "Angular" },
|
|
6264
6883
|
{ name: "None", value: "None" }
|
|
6265
6884
|
],
|
|
6266
6885
|
default: (ctx) => {
|
|
6886
|
+
if (hasDependency(ctx, "@tanstack/start")) return "TanStack Start";
|
|
6267
6887
|
if (hasDependency(ctx, "next")) return "Next.js";
|
|
6268
6888
|
if (hasDependency(ctx, "nuxt")) return "Nuxt";
|
|
6269
6889
|
if (hasDependency(ctx, "vue")) return "Vue";
|
|
@@ -6271,6 +6891,7 @@ var TEMPLATE_PLACEHOLDERS = [
|
|
|
6271
6891
|
if (hasDependency(ctx, "@sveltejs/kit")) return "SvelteKit";
|
|
6272
6892
|
if (hasDependency(ctx, "astro")) return "Astro";
|
|
6273
6893
|
if (hasDependency(ctx, "solid-js")) return "SolidJS";
|
|
6894
|
+
if (hasDependency(ctx, "@remix-run/react")) return "Remix";
|
|
6274
6895
|
if (hasDependency(ctx, "@angular/core")) return "Angular";
|
|
6275
6896
|
if (hasDependency(ctx, "react")) return "React";
|
|
6276
6897
|
return "None";
|
|
@@ -6342,26 +6963,32 @@ var TEMPLATE_PLACEHOLDERS = [
|
|
|
6342
6963
|
description: "Authentication approach",
|
|
6343
6964
|
inputType: "select",
|
|
6344
6965
|
choices: [
|
|
6966
|
+
{ name: "Better Auth", value: "Better Auth" },
|
|
6345
6967
|
{ name: "Clerk", value: "Clerk" },
|
|
6346
6968
|
{ name: "Auth.js (NextAuth)", value: "Auth.js" },
|
|
6347
6969
|
{ name: "Lucia", value: "Lucia" },
|
|
6348
6970
|
{ name: "Firebase Auth", value: "Firebase" },
|
|
6349
6971
|
{ name: "Supabase Auth", value: "Supabase" },
|
|
6972
|
+
{ name: "Kinde", value: "Kinde" },
|
|
6973
|
+
{ name: "WorkOS", value: "WorkOS" },
|
|
6350
6974
|
{ name: "Custom JWT", value: "JWT" },
|
|
6351
6975
|
{ name: "Session-based", value: "Session" },
|
|
6352
6976
|
{ name: "None", value: "None" }
|
|
6353
6977
|
],
|
|
6354
6978
|
default: (ctx) => {
|
|
6979
|
+
if (hasDependency(ctx, "better-auth")) return "Better Auth";
|
|
6355
6980
|
if (hasDependency(ctx, "@clerk/nextjs") || hasDependency(ctx, "@clerk/clerk-react"))
|
|
6356
6981
|
return "Clerk";
|
|
6357
6982
|
if (hasDependency(ctx, "next-auth") || hasDependency(ctx, "@auth/core")) return "Auth.js";
|
|
6358
6983
|
if (hasDependency(ctx, "lucia")) return "Lucia";
|
|
6359
6984
|
if (hasDependency(ctx, "firebase")) return "Firebase";
|
|
6360
6985
|
if (hasDependency(ctx, "@supabase/supabase-js")) return "Supabase";
|
|
6986
|
+
if (hasDependency(ctx, "@kinde-oss/kinde-auth-nextjs")) return "Kinde";
|
|
6987
|
+
if (hasDependency(ctx, "@workos-inc/authkit-nextjs")) return "WorkOS";
|
|
6361
6988
|
return "None";
|
|
6362
6989
|
},
|
|
6363
6990
|
required: false,
|
|
6364
|
-
example: "
|
|
6991
|
+
example: "Better Auth"
|
|
6365
6992
|
},
|
|
6366
6993
|
{
|
|
6367
6994
|
key: "STATE_MANAGEMENT",
|
|
@@ -6738,7 +7365,7 @@ function flattenTemplateConfig(config) {
|
|
|
6738
7365
|
return flattened;
|
|
6739
7366
|
}
|
|
6740
7367
|
function shouldProcessFile(filePath) {
|
|
6741
|
-
const ext =
|
|
7368
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
6742
7369
|
return PROCESSABLE_EXTENSIONS.includes(ext);
|
|
6743
7370
|
}
|
|
6744
7371
|
function shouldSkipDirectory(dirName) {
|
|
@@ -6747,9 +7374,9 @@ function shouldSkipDirectory(dirName) {
|
|
|
6747
7374
|
async function getAllFiles(dir) {
|
|
6748
7375
|
const files = [];
|
|
6749
7376
|
try {
|
|
6750
|
-
const entries = await
|
|
7377
|
+
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
6751
7378
|
for (const entry of entries) {
|
|
6752
|
-
const fullPath =
|
|
7379
|
+
const fullPath = path5.join(dir, entry.name);
|
|
6753
7380
|
if (entry.isDirectory()) {
|
|
6754
7381
|
if (!shouldSkipDirectory(entry.name)) {
|
|
6755
7382
|
const subFiles = await getAllFiles(fullPath);
|
|
@@ -6766,7 +7393,7 @@ async function getAllFiles(dir) {
|
|
|
6766
7393
|
async function replaceInFile2(filePath, replacements) {
|
|
6767
7394
|
const changes = [];
|
|
6768
7395
|
try {
|
|
6769
|
-
let content = await
|
|
7396
|
+
let content = await fs4.readFile(filePath, "utf-8");
|
|
6770
7397
|
let modified = false;
|
|
6771
7398
|
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
6772
7399
|
if (content.includes(placeholder)) {
|
|
@@ -6776,7 +7403,7 @@ async function replaceInFile2(filePath, replacements) {
|
|
|
6776
7403
|
}
|
|
6777
7404
|
}
|
|
6778
7405
|
if (modified) {
|
|
6779
|
-
await
|
|
7406
|
+
await fs4.writeFile(filePath, content, "utf-8");
|
|
6780
7407
|
}
|
|
6781
7408
|
} catch {
|
|
6782
7409
|
}
|
|
@@ -6797,7 +7424,7 @@ async function replaceTemplatePlaceholders(dir, config) {
|
|
|
6797
7424
|
report.filesModified++;
|
|
6798
7425
|
for (const change of changes) {
|
|
6799
7426
|
report.replacements.push({
|
|
6800
|
-
file:
|
|
7427
|
+
file: path5.relative(dir, file),
|
|
6801
7428
|
placeholder: change.placeholder,
|
|
6802
7429
|
value: change.value
|
|
6803
7430
|
});
|
|
@@ -6868,11 +7495,11 @@ async function previewReplacements(dir, config) {
|
|
|
6868
7495
|
const preview = [];
|
|
6869
7496
|
for (const file of files) {
|
|
6870
7497
|
try {
|
|
6871
|
-
const content = await
|
|
7498
|
+
const content = await fs4.readFile(file, "utf-8");
|
|
6872
7499
|
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
6873
7500
|
if (content.includes(placeholder)) {
|
|
6874
7501
|
preview.push({
|
|
6875
|
-
file:
|
|
7502
|
+
file: path5.relative(dir, file),
|
|
6876
7503
|
placeholder,
|
|
6877
7504
|
value
|
|
6878
7505
|
});
|
|
@@ -6887,27 +7514,6 @@ async function previewReplacements(dir, config) {
|
|
|
6887
7514
|
// src/cli/commands/init.ts
|
|
6888
7515
|
init_fs();
|
|
6889
7516
|
|
|
6890
|
-
// src/lib/utils/paths.ts
|
|
6891
|
-
init_cjs_shims();
|
|
6892
|
-
var import_node_fs = __toESM(require("fs"), 1);
|
|
6893
|
-
var import_node_path3 = __toESM(require("path"), 1);
|
|
6894
|
-
var import_node_url = require("url");
|
|
6895
|
-
function getPackageRoot() {
|
|
6896
|
-
const currentFilePath = (0, import_node_url.fileURLToPath)(importMetaUrl);
|
|
6897
|
-
let currentDir = import_node_path3.default.dirname(currentFilePath);
|
|
6898
|
-
while (currentDir !== import_node_path3.default.dirname(currentDir)) {
|
|
6899
|
-
const packageJsonPath = import_node_path3.default.join(currentDir, "package.json");
|
|
6900
|
-
if (import_node_fs.default.existsSync(packageJsonPath)) {
|
|
6901
|
-
return currentDir;
|
|
6902
|
-
}
|
|
6903
|
-
currentDir = import_node_path3.default.dirname(currentDir);
|
|
6904
|
-
}
|
|
6905
|
-
throw new Error("Could not find package root (no package.json found in parent directories)");
|
|
6906
|
-
}
|
|
6907
|
-
function getTemplatesPath() {
|
|
6908
|
-
return import_node_path3.default.join(getPackageRoot(), "templates");
|
|
6909
|
-
}
|
|
6910
|
-
|
|
6911
7517
|
// src/lib/utils/prompt-cancel.ts
|
|
6912
7518
|
init_cjs_shims();
|
|
6913
7519
|
var readline = __toESM(require("readline"), 1);
|
|
@@ -7379,6 +7985,124 @@ async function editBundleSelection(currentBundleIds) {
|
|
|
7379
7985
|
return selected;
|
|
7380
7986
|
}
|
|
7381
7987
|
|
|
7988
|
+
// src/cli/prompts/ci-cd-config.ts
|
|
7989
|
+
init_cjs_shims();
|
|
7990
|
+
async function promptCICDConfig(options) {
|
|
7991
|
+
logger.section("CI/CD Configuration", "\u{1F680}");
|
|
7992
|
+
logger.info("Configure continuous integration and deployment workflows");
|
|
7993
|
+
logger.newline();
|
|
7994
|
+
const enableCICD = await confirm({
|
|
7995
|
+
message: "Would you like to set up GitHub Actions workflows?",
|
|
7996
|
+
default: true
|
|
7997
|
+
});
|
|
7998
|
+
if (!enableCICD) {
|
|
7999
|
+
return {
|
|
8000
|
+
enabled: false,
|
|
8001
|
+
provider: "github-actions",
|
|
8002
|
+
ci: false,
|
|
8003
|
+
cd: false,
|
|
8004
|
+
packageManager: options?.packageManager || "pnpm",
|
|
8005
|
+
nodeVersion: "22",
|
|
8006
|
+
enableCaching: true,
|
|
8007
|
+
runTests: true,
|
|
8008
|
+
runLint: true,
|
|
8009
|
+
runTypecheck: true,
|
|
8010
|
+
runBuild: true
|
|
8011
|
+
};
|
|
8012
|
+
}
|
|
8013
|
+
const workflows = await checkbox({
|
|
8014
|
+
message: "Which workflows would you like to create?",
|
|
8015
|
+
choices: [
|
|
8016
|
+
{
|
|
8017
|
+
name: "CI (Continuous Integration) - Lint, test, build on PRs",
|
|
8018
|
+
value: "ci",
|
|
8019
|
+
checked: true
|
|
8020
|
+
},
|
|
8021
|
+
{
|
|
8022
|
+
name: "Release - Create releases on version tags",
|
|
8023
|
+
value: "cd",
|
|
8024
|
+
checked: false
|
|
8025
|
+
}
|
|
8026
|
+
]
|
|
8027
|
+
});
|
|
8028
|
+
if (workflows.length === 0) {
|
|
8029
|
+
return {
|
|
8030
|
+
enabled: false,
|
|
8031
|
+
provider: "github-actions",
|
|
8032
|
+
ci: false,
|
|
8033
|
+
cd: false,
|
|
8034
|
+
packageManager: options?.packageManager || "pnpm",
|
|
8035
|
+
nodeVersion: "22",
|
|
8036
|
+
enableCaching: true,
|
|
8037
|
+
runTests: true,
|
|
8038
|
+
runLint: true,
|
|
8039
|
+
runTypecheck: true,
|
|
8040
|
+
runBuild: true
|
|
8041
|
+
};
|
|
8042
|
+
}
|
|
8043
|
+
const hasCi = workflows.includes("ci");
|
|
8044
|
+
const hasCd = workflows.includes("cd");
|
|
8045
|
+
let runTests = true;
|
|
8046
|
+
let runLint = true;
|
|
8047
|
+
let runTypecheck = true;
|
|
8048
|
+
let runBuild = true;
|
|
8049
|
+
if (hasCi) {
|
|
8050
|
+
const ciSteps = await checkbox({
|
|
8051
|
+
message: "Which steps should the CI workflow run?",
|
|
8052
|
+
choices: [
|
|
8053
|
+
{ name: "Lint", value: "lint", checked: true },
|
|
8054
|
+
{ name: "Type checking", value: "typecheck", checked: true },
|
|
8055
|
+
{ name: "Tests", value: "tests", checked: true },
|
|
8056
|
+
{ name: "Build", value: "build", checked: true }
|
|
8057
|
+
]
|
|
8058
|
+
});
|
|
8059
|
+
runTests = ciSteps.includes("tests");
|
|
8060
|
+
runLint = ciSteps.includes("lint");
|
|
8061
|
+
runTypecheck = ciSteps.includes("typecheck");
|
|
8062
|
+
runBuild = ciSteps.includes("build");
|
|
8063
|
+
}
|
|
8064
|
+
const nodeVersion = await select({
|
|
8065
|
+
message: "Node.js version:",
|
|
8066
|
+
choices: [
|
|
8067
|
+
{ name: "22 (LTS - Recommended)", value: "22" },
|
|
8068
|
+
{ name: "20 (LTS)", value: "20" },
|
|
8069
|
+
{ name: "18 (LTS)", value: "18" },
|
|
8070
|
+
{ name: "Custom", value: "custom" }
|
|
8071
|
+
],
|
|
8072
|
+
default: "22"
|
|
8073
|
+
});
|
|
8074
|
+
let finalNodeVersion = nodeVersion;
|
|
8075
|
+
if (nodeVersion === "custom") {
|
|
8076
|
+
finalNodeVersion = await input({
|
|
8077
|
+
message: "Enter Node.js version:",
|
|
8078
|
+
default: "22",
|
|
8079
|
+
validate: (v) => {
|
|
8080
|
+
if (!/^\d+(\.\d+)?$/.test(v)) {
|
|
8081
|
+
return 'Please enter a valid version (e.g., "20" or "20.10")';
|
|
8082
|
+
}
|
|
8083
|
+
return true;
|
|
8084
|
+
}
|
|
8085
|
+
});
|
|
8086
|
+
}
|
|
8087
|
+
const enableCaching = await confirm({
|
|
8088
|
+
message: "Enable dependency caching for faster builds?",
|
|
8089
|
+
default: true
|
|
8090
|
+
});
|
|
8091
|
+
return {
|
|
8092
|
+
enabled: true,
|
|
8093
|
+
provider: "github-actions",
|
|
8094
|
+
ci: hasCi,
|
|
8095
|
+
cd: hasCd,
|
|
8096
|
+
packageManager: options?.packageManager || "pnpm",
|
|
8097
|
+
nodeVersion: finalNodeVersion,
|
|
8098
|
+
enableCaching,
|
|
8099
|
+
runTests,
|
|
8100
|
+
runLint,
|
|
8101
|
+
runTypecheck,
|
|
8102
|
+
runBuild
|
|
8103
|
+
};
|
|
8104
|
+
}
|
|
8105
|
+
|
|
7382
8106
|
// src/cli/prompts/folder-preferences.ts
|
|
7383
8107
|
init_cjs_shims();
|
|
7384
8108
|
|
|
@@ -7993,18 +8717,38 @@ async function promptProjectInfo(options) {
|
|
|
7993
8717
|
return true;
|
|
7994
8718
|
}
|
|
7995
8719
|
});
|
|
7996
|
-
const
|
|
7997
|
-
message:
|
|
7998
|
-
default: options?.defaults?.
|
|
7999
|
-
validate: (value) => {
|
|
8000
|
-
if (!value.trim()) return "Entity type is required";
|
|
8001
|
-
return true;
|
|
8002
|
-
}
|
|
8720
|
+
const author = await input({
|
|
8721
|
+
message: 'Author (name or "Name <email>"):',
|
|
8722
|
+
default: options?.defaults?.author || ""
|
|
8003
8723
|
});
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8724
|
+
let entityType = "item";
|
|
8725
|
+
let entityTypePlural = "items";
|
|
8726
|
+
const wantEntityConfig = await confirm({
|
|
8727
|
+
message: "Configure primary entity type? (Used for code examples and templates)",
|
|
8728
|
+
default: false
|
|
8007
8729
|
});
|
|
8730
|
+
if (wantEntityConfig) {
|
|
8731
|
+
logger.info("The entity type is used in code examples and templates throughout the project.");
|
|
8732
|
+
logger.info(
|
|
8733
|
+
'For example, if your project manages "products", code examples will use product-related names.'
|
|
8734
|
+
);
|
|
8735
|
+
logger.newline();
|
|
8736
|
+
entityType = await input({
|
|
8737
|
+
message: "Primary entity type (e.g., product, article, user, listing):",
|
|
8738
|
+
default: options?.defaults?.entityType || "item",
|
|
8739
|
+
validate: (value) => {
|
|
8740
|
+
if (!value.trim()) return "Entity type is required";
|
|
8741
|
+
return true;
|
|
8742
|
+
}
|
|
8743
|
+
});
|
|
8744
|
+
entityTypePlural = await input({
|
|
8745
|
+
message: "Entity type plural:",
|
|
8746
|
+
default: options?.defaults?.entityTypePlural || pluralize(entityType)
|
|
8747
|
+
});
|
|
8748
|
+
} else if (options?.defaults?.entityType) {
|
|
8749
|
+
entityType = options.defaults.entityType;
|
|
8750
|
+
entityTypePlural = options.defaults.entityTypePlural || pluralize(entityType);
|
|
8751
|
+
}
|
|
8008
8752
|
let domain;
|
|
8009
8753
|
let location;
|
|
8010
8754
|
if (!options?.skipOptional) {
|
|
@@ -8043,7 +8787,8 @@ async function promptProjectInfo(options) {
|
|
|
8043
8787
|
domain,
|
|
8044
8788
|
entityType: entityType.trim().toLowerCase(),
|
|
8045
8789
|
entityTypePlural: entityTypePlural.trim().toLowerCase(),
|
|
8046
|
-
location
|
|
8790
|
+
location,
|
|
8791
|
+
author: author.trim() || void 0
|
|
8047
8792
|
};
|
|
8048
8793
|
}
|
|
8049
8794
|
function pluralize(word) {
|
|
@@ -8062,6 +8807,7 @@ async function confirmProjectInfo(info) {
|
|
|
8062
8807
|
logger.keyValue("Name", info.name);
|
|
8063
8808
|
logger.keyValue("Description", info.description);
|
|
8064
8809
|
logger.keyValue("GitHub", `${info.org}/${info.repo}`);
|
|
8810
|
+
if (info.author) logger.keyValue("Author", info.author);
|
|
8065
8811
|
logger.keyValue("Entity", `${info.entityType} / ${info.entityTypePlural}`);
|
|
8066
8812
|
if (info.domain) logger.keyValue("Domain", info.domain);
|
|
8067
8813
|
if (info.location) logger.keyValue("Location", info.location);
|
|
@@ -10010,6 +10756,17 @@ function placeholderKeyToConfigKey(key, category) {
|
|
|
10010
10756
|
async function promptTemplateConfig(options) {
|
|
10011
10757
|
const { context, mode = "quick", category, requiredOnly = false } = options;
|
|
10012
10758
|
logger.section("Template Configuration", "\u{1F4DD}");
|
|
10759
|
+
logger.newline();
|
|
10760
|
+
logger.info("This step personalizes all Claude Code configuration files for YOUR project.");
|
|
10761
|
+
logger.info("Your answers will be used to:");
|
|
10762
|
+
logger.newline();
|
|
10763
|
+
logger.item("Replace {{PLACEHOLDERS}} in agents, commands, and skills");
|
|
10764
|
+
logger.item("Configure CLAUDE.md with your tech stack and commands");
|
|
10765
|
+
logger.item("Set up quality targets (coverage, performance, accessibility)");
|
|
10766
|
+
logger.item("Customize code examples to match your project structure");
|
|
10767
|
+
logger.newline();
|
|
10768
|
+
logger.info("Accurate configuration means better AI assistance tailored to your codebase!");
|
|
10769
|
+
logger.newline();
|
|
10013
10770
|
const hasDefaults = await hasGlobalDefaults();
|
|
10014
10771
|
const globalDefaults = hasDefaults ? await getGlobalTemplateConfig() : void 0;
|
|
10015
10772
|
if (mode === "quick") {
|
|
@@ -10282,7 +11039,7 @@ async function runInit(path7, options) {
|
|
|
10282
11039
|
logger.warn("Configuration cancelled");
|
|
10283
11040
|
return;
|
|
10284
11041
|
}
|
|
10285
|
-
const { config, skippedMcpConfigs, templateConfig } = buildResult;
|
|
11042
|
+
const { config, skippedMcpConfigs, templateConfig, cicdConfig } = buildResult;
|
|
10286
11043
|
if (templateConfig) {
|
|
10287
11044
|
config.templateConfig = templateConfig;
|
|
10288
11045
|
}
|
|
@@ -10299,7 +11056,7 @@ async function runInit(path7, options) {
|
|
|
10299
11056
|
showConfigSummary(config);
|
|
10300
11057
|
return;
|
|
10301
11058
|
}
|
|
10302
|
-
await executeInstallation(projectPath, config, registry, templatesPath, options);
|
|
11059
|
+
await executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig);
|
|
10303
11060
|
if (templateConfig && !options.noPlaceholders) {
|
|
10304
11061
|
const claudePath = joinPath(projectPath, ".claude");
|
|
10305
11062
|
await replaceTemplateConfigWithSpinner(claudePath, templateConfig);
|
|
@@ -10479,6 +11236,9 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
|
|
|
10479
11236
|
}
|
|
10480
11237
|
const permissionsConfig = await promptPermissionsConfig();
|
|
10481
11238
|
const codeStyleConfig = await promptCodeStyleConfig();
|
|
11239
|
+
const cicdConfig = await promptCICDConfig({
|
|
11240
|
+
packageManager: preferences.packageManager
|
|
11241
|
+
});
|
|
10482
11242
|
const folderPreferences = await promptQuickFolderPreferences({
|
|
10483
11243
|
selectedBundles: bundleSelection.selectedBundles,
|
|
10484
11244
|
technologies: detection.detectedTechnologies || []
|
|
@@ -10536,7 +11296,8 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
|
|
|
10536
11296
|
return {
|
|
10537
11297
|
config,
|
|
10538
11298
|
skippedMcpConfigs,
|
|
10539
|
-
templateConfig: templateConfigResult
|
|
11299
|
+
templateConfig: templateConfigResult,
|
|
11300
|
+
cicdConfig
|
|
10540
11301
|
};
|
|
10541
11302
|
}
|
|
10542
11303
|
async function selectModulesWithBundles(registry, suggestedBundles) {
|
|
@@ -10584,7 +11345,7 @@ async function selectModulesWithBundles(registry, suggestedBundles) {
|
|
|
10584
11345
|
}
|
|
10585
11346
|
return result;
|
|
10586
11347
|
}
|
|
10587
|
-
async function executeInstallation(projectPath, config, registry, templatesPath, options) {
|
|
11348
|
+
async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig) {
|
|
10588
11349
|
logger.newline();
|
|
10589
11350
|
logger.title("Installing Configuration");
|
|
10590
11351
|
if (config.scaffold.type === "full-project") {
|
|
@@ -10596,6 +11357,16 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
|
|
|
10596
11357
|
...scaffoldResult.createdFiles
|
|
10597
11358
|
];
|
|
10598
11359
|
}
|
|
11360
|
+
const claudeMdResult = await generateClaudeMdWithSpinner(projectPath, config.project, {
|
|
11361
|
+
overwrite: options.force,
|
|
11362
|
+
templateConfig: config.templateConfig,
|
|
11363
|
+
claudeConfig: config
|
|
11364
|
+
});
|
|
11365
|
+
if (claudeMdResult.error) {
|
|
11366
|
+
logger.warn(`CLAUDE.md generation warning: ${claudeMdResult.error}`);
|
|
11367
|
+
} else if (claudeMdResult.skipped) {
|
|
11368
|
+
logger.info("CLAUDE.md already exists, skipped");
|
|
11369
|
+
}
|
|
10599
11370
|
const modulesByCategory = {
|
|
10600
11371
|
agents: filterModules(registry, "agents", config.modules.agents.selected),
|
|
10601
11372
|
skills: filterModules(registry, "skills", config.modules.skills.selected),
|
|
@@ -10656,6 +11427,33 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
|
|
|
10656
11427
|
if (codeStyleResult.errors.length > 0) {
|
|
10657
11428
|
logger.warn(`Code style installation warnings: ${codeStyleResult.errors.join(", ")}`);
|
|
10658
11429
|
}
|
|
11430
|
+
const vscodeResult = await installVSCodeConfig(projectPath, config.extras.codeStyle, {
|
|
11431
|
+
overwrite: options.force,
|
|
11432
|
+
merge: !options.force
|
|
11433
|
+
// Merge with existing settings if not forcing
|
|
11434
|
+
});
|
|
11435
|
+
if (vscodeResult.settings.error) {
|
|
11436
|
+
logger.warn(`VSCode settings warning: ${vscodeResult.settings.error}`);
|
|
11437
|
+
} else if (vscodeResult.settings.created || vscodeResult.settings.updated) {
|
|
11438
|
+
logger.info("VSCode settings configured for code style tools");
|
|
11439
|
+
}
|
|
11440
|
+
const huskyConfig = deriveHuskyConfigFromCodeStyle(config.extras.codeStyle);
|
|
11441
|
+
if (huskyConfig) {
|
|
11442
|
+
const huskyResult = await installHuskyWithSpinner(projectPath, huskyConfig, {
|
|
11443
|
+
overwrite: options.force
|
|
11444
|
+
});
|
|
11445
|
+
if (huskyResult.errors.length > 0) {
|
|
11446
|
+
logger.warn(`Husky installation warnings: ${huskyResult.errors.join(", ")}`);
|
|
11447
|
+
}
|
|
11448
|
+
}
|
|
11449
|
+
}
|
|
11450
|
+
if (cicdConfig?.enabled) {
|
|
11451
|
+
const cicdResult = await installCICDWithSpinner(projectPath, cicdConfig, {
|
|
11452
|
+
overwrite: options.force
|
|
11453
|
+
});
|
|
11454
|
+
if (cicdResult.errors.length > 0) {
|
|
11455
|
+
logger.warn(`CI/CD installation warnings: ${cicdResult.errors.join(", ")}`);
|
|
11456
|
+
}
|
|
10659
11457
|
}
|
|
10660
11458
|
await writeConfig(projectPath, config);
|
|
10661
11459
|
logger.newline();
|
|
@@ -10684,6 +11482,7 @@ async function handlePackageJsonUpdate(projectPath, config, options) {
|
|
|
10684
11482
|
project: {
|
|
10685
11483
|
name: config.project.name,
|
|
10686
11484
|
description: config.project.description,
|
|
11485
|
+
author: config.project.author,
|
|
10687
11486
|
repository: config.project.org && config.project.repo ? `https://github.com/${config.project.org}/${config.project.repo}` : void 0
|
|
10688
11487
|
}
|
|
10689
11488
|
};
|
|
@@ -10753,7 +11552,7 @@ async function handlePackageJsonUpdate(projectPath, config, options) {
|
|
|
10753
11552
|
logger.newline();
|
|
10754
11553
|
logger.subtitle("Next Steps");
|
|
10755
11554
|
logger.info("Run the following command to install dependencies:");
|
|
10756
|
-
logger.raw(` ${
|
|
11555
|
+
logger.raw(` ${getInstallCommand2(packageManager)}`);
|
|
10757
11556
|
const setupInstructions = getSetupInstructions(toolSelection);
|
|
10758
11557
|
if (setupInstructions.length > 0) {
|
|
10759
11558
|
logger.newline();
|