@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.js
CHANGED
|
@@ -49,7 +49,7 @@ __export(fs_exports, {
|
|
|
49
49
|
writeFile: () => writeFile,
|
|
50
50
|
writeJson: () => writeJson
|
|
51
51
|
});
|
|
52
|
-
import
|
|
52
|
+
import path2 from "path";
|
|
53
53
|
import fs from "fs-extra";
|
|
54
54
|
import { glob } from "glob";
|
|
55
55
|
async function pathExists(filePath) {
|
|
@@ -75,18 +75,18 @@ async function readJson(filePath) {
|
|
|
75
75
|
return fs.readJson(filePath);
|
|
76
76
|
}
|
|
77
77
|
async function writeJson(filePath, data, options) {
|
|
78
|
-
await fs.ensureDir(
|
|
78
|
+
await fs.ensureDir(path2.dirname(filePath));
|
|
79
79
|
await fs.writeJson(filePath, data, { spaces: options?.spaces ?? 2 });
|
|
80
80
|
}
|
|
81
81
|
async function readFile(filePath) {
|
|
82
82
|
return fs.readFile(filePath, "utf-8");
|
|
83
83
|
}
|
|
84
84
|
async function writeFile(filePath, content) {
|
|
85
|
-
await fs.ensureDir(
|
|
85
|
+
await fs.ensureDir(path2.dirname(filePath));
|
|
86
86
|
await fs.writeFile(filePath, content, "utf-8");
|
|
87
87
|
}
|
|
88
88
|
async function copy(src, dest, options) {
|
|
89
|
-
await fs.ensureDir(
|
|
89
|
+
await fs.ensureDir(path2.dirname(dest));
|
|
90
90
|
await fs.copy(src, dest, { overwrite: options?.overwrite ?? false });
|
|
91
91
|
}
|
|
92
92
|
async function copyDir(src, dest, options) {
|
|
@@ -114,7 +114,7 @@ async function listDirs(pattern, options) {
|
|
|
114
114
|
});
|
|
115
115
|
const dirs = [];
|
|
116
116
|
for (const match of matches) {
|
|
117
|
-
const fullPath = options?.cwd ?
|
|
117
|
+
const fullPath = options?.cwd ? path2.join(options.cwd, match) : match;
|
|
118
118
|
if (await isDirectory(fullPath)) {
|
|
119
119
|
dirs.push(match);
|
|
120
120
|
}
|
|
@@ -156,22 +156,22 @@ async function filesAreEqual(file1, file2) {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
function relativePath(from, to) {
|
|
159
|
-
return
|
|
159
|
+
return path2.relative(from, to);
|
|
160
160
|
}
|
|
161
161
|
function resolvePath(...segments) {
|
|
162
|
-
return
|
|
162
|
+
return path2.resolve(...segments);
|
|
163
163
|
}
|
|
164
164
|
function joinPath(...segments) {
|
|
165
|
-
return
|
|
165
|
+
return path2.join(...segments);
|
|
166
166
|
}
|
|
167
167
|
function dirname(filePath) {
|
|
168
|
-
return
|
|
168
|
+
return path2.dirname(filePath);
|
|
169
169
|
}
|
|
170
170
|
function basename(filePath, ext) {
|
|
171
|
-
return
|
|
171
|
+
return path2.basename(filePath, ext);
|
|
172
172
|
}
|
|
173
173
|
function extname(filePath) {
|
|
174
|
-
return
|
|
174
|
+
return path2.extname(filePath);
|
|
175
175
|
}
|
|
176
176
|
async function backup(src, suffix = ".backup") {
|
|
177
177
|
const backupPath = `${src}${suffix}`;
|
|
@@ -187,7 +187,7 @@ async function makeExecutable(filePath) {
|
|
|
187
187
|
async function createTempDir(prefix = "claude-config-") {
|
|
188
188
|
const os4 = await import("os");
|
|
189
189
|
const tempBase = os4.tmpdir();
|
|
190
|
-
const tempDir =
|
|
190
|
+
const tempDir = path2.join(tempBase, `${prefix}${Date.now()}`);
|
|
191
191
|
await ensureDir(tempDir);
|
|
192
192
|
return tempDir;
|
|
193
193
|
}
|
|
@@ -1327,12 +1327,350 @@ function getBundleCategoryName(category) {
|
|
|
1327
1327
|
return BUNDLE_CATEGORY_NAMES[category] ?? category;
|
|
1328
1328
|
}
|
|
1329
1329
|
|
|
1330
|
+
// src/lib/ci-cd/index.ts
|
|
1331
|
+
init_esm_shims();
|
|
1332
|
+
|
|
1333
|
+
// src/lib/ci-cd/github-actions-generator.ts
|
|
1334
|
+
init_esm_shims();
|
|
1335
|
+
init_fs();
|
|
1336
|
+
|
|
1337
|
+
// src/lib/utils/spinner.ts
|
|
1338
|
+
init_esm_shims();
|
|
1339
|
+
import ora from "ora";
|
|
1340
|
+
import pc from "picocolors";
|
|
1341
|
+
var SpinnerManager = class {
|
|
1342
|
+
spinner = null;
|
|
1343
|
+
silent = false;
|
|
1344
|
+
configure(options) {
|
|
1345
|
+
if (options.silent !== void 0) this.silent = options.silent;
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Start a spinner with a message
|
|
1349
|
+
*/
|
|
1350
|
+
start(text, options) {
|
|
1351
|
+
if (this.silent) return null;
|
|
1352
|
+
this.stop();
|
|
1353
|
+
this.spinner = ora({
|
|
1354
|
+
text,
|
|
1355
|
+
color: options?.color || "cyan",
|
|
1356
|
+
spinner: "dots"
|
|
1357
|
+
}).start();
|
|
1358
|
+
return this.spinner;
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Update spinner text
|
|
1362
|
+
*/
|
|
1363
|
+
text(text) {
|
|
1364
|
+
if (this.spinner) {
|
|
1365
|
+
this.spinner.text = text;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Stop spinner with success message
|
|
1370
|
+
*/
|
|
1371
|
+
succeed(text) {
|
|
1372
|
+
if (this.spinner) {
|
|
1373
|
+
this.spinner.succeed(text);
|
|
1374
|
+
this.spinner = null;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Stop spinner with failure message
|
|
1379
|
+
*/
|
|
1380
|
+
fail(text) {
|
|
1381
|
+
if (this.spinner) {
|
|
1382
|
+
this.spinner.fail(text);
|
|
1383
|
+
this.spinner = null;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Stop spinner with warning message
|
|
1388
|
+
*/
|
|
1389
|
+
warn(text) {
|
|
1390
|
+
if (this.spinner) {
|
|
1391
|
+
this.spinner.warn(text);
|
|
1392
|
+
this.spinner = null;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Stop spinner with info message
|
|
1397
|
+
*/
|
|
1398
|
+
info(text) {
|
|
1399
|
+
if (this.spinner) {
|
|
1400
|
+
this.spinner.info(text);
|
|
1401
|
+
this.spinner = null;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Stop spinner without message
|
|
1406
|
+
*/
|
|
1407
|
+
stop() {
|
|
1408
|
+
if (this.spinner) {
|
|
1409
|
+
this.spinner.stop();
|
|
1410
|
+
this.spinner = null;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Check if spinner is running
|
|
1415
|
+
*/
|
|
1416
|
+
isRunning() {
|
|
1417
|
+
return this.spinner?.isSpinning ?? false;
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
var spinner = new SpinnerManager();
|
|
1421
|
+
async function withSpinner(text, operation, options) {
|
|
1422
|
+
if (options?.silent) {
|
|
1423
|
+
return operation();
|
|
1424
|
+
}
|
|
1425
|
+
spinner.start(text);
|
|
1426
|
+
try {
|
|
1427
|
+
const result = await operation();
|
|
1428
|
+
spinner.succeed(options?.successText || text);
|
|
1429
|
+
return result;
|
|
1430
|
+
} catch (error) {
|
|
1431
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1432
|
+
spinner.fail(options?.failText || `${text} - ${pc.red(errorMessage)}`);
|
|
1433
|
+
throw error;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// src/lib/ci-cd/github-actions-generator.ts
|
|
1438
|
+
function getInstallCommand(packageManager) {
|
|
1439
|
+
switch (packageManager) {
|
|
1440
|
+
case "npm":
|
|
1441
|
+
return "npm ci";
|
|
1442
|
+
case "yarn":
|
|
1443
|
+
return "yarn install --frozen-lockfile";
|
|
1444
|
+
case "pnpm":
|
|
1445
|
+
return "pnpm install --frozen-lockfile";
|
|
1446
|
+
case "bun":
|
|
1447
|
+
return "bun install --frozen-lockfile";
|
|
1448
|
+
default:
|
|
1449
|
+
return "npm ci";
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
function getRunCommand(packageManager, script) {
|
|
1453
|
+
switch (packageManager) {
|
|
1454
|
+
case "npm":
|
|
1455
|
+
return `npm run ${script}`;
|
|
1456
|
+
case "yarn":
|
|
1457
|
+
return `yarn ${script}`;
|
|
1458
|
+
case "pnpm":
|
|
1459
|
+
return `pnpm ${script}`;
|
|
1460
|
+
case "bun":
|
|
1461
|
+
return `bun run ${script}`;
|
|
1462
|
+
default:
|
|
1463
|
+
return `npm run ${script}`;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
function getCacheConfig(packageManager) {
|
|
1467
|
+
switch (packageManager) {
|
|
1468
|
+
case "npm":
|
|
1469
|
+
return {
|
|
1470
|
+
path: "~/.npm",
|
|
1471
|
+
key: "npm-${{ hashFiles('**/package-lock.json') }}"
|
|
1472
|
+
};
|
|
1473
|
+
case "yarn":
|
|
1474
|
+
return {
|
|
1475
|
+
path: ".yarn/cache",
|
|
1476
|
+
key: "yarn-${{ hashFiles('**/yarn.lock') }}"
|
|
1477
|
+
};
|
|
1478
|
+
case "pnpm":
|
|
1479
|
+
return {
|
|
1480
|
+
path: "~/.local/share/pnpm/store",
|
|
1481
|
+
key: "pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}"
|
|
1482
|
+
};
|
|
1483
|
+
case "bun":
|
|
1484
|
+
return {
|
|
1485
|
+
path: "~/.bun/install/cache",
|
|
1486
|
+
key: "bun-${{ hashFiles('**/bun.lockb') }}"
|
|
1487
|
+
};
|
|
1488
|
+
default:
|
|
1489
|
+
return {
|
|
1490
|
+
path: "~/.npm",
|
|
1491
|
+
key: "npm-${{ hashFiles('**/package-lock.json') }}"
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
function generateCIWorkflow(config) {
|
|
1496
|
+
const { packageManager, nodeVersion, enableCaching, runTests, runLint, runTypecheck, runBuild } = config;
|
|
1497
|
+
const cache = getCacheConfig(packageManager);
|
|
1498
|
+
const installCmd = getInstallCommand(packageManager);
|
|
1499
|
+
const steps = [];
|
|
1500
|
+
steps.push(` - name: Checkout
|
|
1501
|
+
uses: actions/checkout@v4`);
|
|
1502
|
+
if (packageManager === "pnpm") {
|
|
1503
|
+
steps.push(`
|
|
1504
|
+
- name: Setup pnpm
|
|
1505
|
+
uses: pnpm/action-setup@v4
|
|
1506
|
+
with:
|
|
1507
|
+
version: latest`);
|
|
1508
|
+
}
|
|
1509
|
+
if (packageManager === "bun") {
|
|
1510
|
+
steps.push(`
|
|
1511
|
+
- name: Setup Bun
|
|
1512
|
+
uses: oven-sh/setup-bun@v2
|
|
1513
|
+
with:
|
|
1514
|
+
bun-version: latest`);
|
|
1515
|
+
}
|
|
1516
|
+
if (packageManager !== "bun") {
|
|
1517
|
+
steps.push(`
|
|
1518
|
+
- name: Setup Node.js
|
|
1519
|
+
uses: actions/setup-node@v4
|
|
1520
|
+
with:
|
|
1521
|
+
node-version: '${nodeVersion}'${enableCaching ? `
|
|
1522
|
+
cache: '${packageManager}'` : ""}`);
|
|
1523
|
+
}
|
|
1524
|
+
if (enableCaching && packageManager === "bun") {
|
|
1525
|
+
steps.push(`
|
|
1526
|
+
- name: Cache dependencies
|
|
1527
|
+
uses: actions/cache@v4
|
|
1528
|
+
with:
|
|
1529
|
+
path: ${cache.path}
|
|
1530
|
+
key: ${cache.key}`);
|
|
1531
|
+
}
|
|
1532
|
+
steps.push(`
|
|
1533
|
+
- name: Install dependencies
|
|
1534
|
+
run: ${installCmd}`);
|
|
1535
|
+
if (runLint) {
|
|
1536
|
+
steps.push(`
|
|
1537
|
+
- name: Lint
|
|
1538
|
+
run: ${getRunCommand(packageManager, "lint")}`);
|
|
1539
|
+
}
|
|
1540
|
+
if (runTypecheck) {
|
|
1541
|
+
steps.push(`
|
|
1542
|
+
- name: Type check
|
|
1543
|
+
run: ${getRunCommand(packageManager, "typecheck")}`);
|
|
1544
|
+
}
|
|
1545
|
+
if (runTests) {
|
|
1546
|
+
steps.push(`
|
|
1547
|
+
- name: Run tests
|
|
1548
|
+
run: ${getRunCommand(packageManager, "test")}`);
|
|
1549
|
+
}
|
|
1550
|
+
if (runBuild) {
|
|
1551
|
+
steps.push(`
|
|
1552
|
+
- name: Build
|
|
1553
|
+
run: ${getRunCommand(packageManager, "build")}`);
|
|
1554
|
+
}
|
|
1555
|
+
return `name: CI
|
|
1556
|
+
|
|
1557
|
+
on:
|
|
1558
|
+
push:
|
|
1559
|
+
branches: [main, master]
|
|
1560
|
+
pull_request:
|
|
1561
|
+
branches: [main, master]
|
|
1562
|
+
|
|
1563
|
+
jobs:
|
|
1564
|
+
ci:
|
|
1565
|
+
runs-on: ubuntu-latest
|
|
1566
|
+
|
|
1567
|
+
steps:
|
|
1568
|
+
${steps.join("\n")}
|
|
1569
|
+
`;
|
|
1570
|
+
}
|
|
1571
|
+
function generateReleaseWorkflow(config) {
|
|
1572
|
+
const { packageManager, nodeVersion } = config;
|
|
1573
|
+
const installCmd = getInstallCommand(packageManager);
|
|
1574
|
+
let setupSteps = "";
|
|
1575
|
+
if (packageManager === "pnpm") {
|
|
1576
|
+
setupSteps = `
|
|
1577
|
+
- name: Setup pnpm
|
|
1578
|
+
uses: pnpm/action-setup@v4
|
|
1579
|
+
with:
|
|
1580
|
+
version: latest
|
|
1581
|
+
`;
|
|
1582
|
+
}
|
|
1583
|
+
return `name: Release
|
|
1584
|
+
|
|
1585
|
+
on:
|
|
1586
|
+
push:
|
|
1587
|
+
tags:
|
|
1588
|
+
- 'v*'
|
|
1589
|
+
|
|
1590
|
+
jobs:
|
|
1591
|
+
release:
|
|
1592
|
+
runs-on: ubuntu-latest
|
|
1593
|
+
permissions:
|
|
1594
|
+
contents: write
|
|
1595
|
+
|
|
1596
|
+
steps:
|
|
1597
|
+
- name: Checkout
|
|
1598
|
+
uses: actions/checkout@v4
|
|
1599
|
+
with:
|
|
1600
|
+
fetch-depth: 0
|
|
1601
|
+
${setupSteps}
|
|
1602
|
+
- name: Setup Node.js
|
|
1603
|
+
uses: actions/setup-node@v4
|
|
1604
|
+
with:
|
|
1605
|
+
node-version: '${nodeVersion}'
|
|
1606
|
+
cache: '${packageManager}'
|
|
1607
|
+
registry-url: 'https://registry.npmjs.org'
|
|
1608
|
+
|
|
1609
|
+
- name: Install dependencies
|
|
1610
|
+
run: ${installCmd}
|
|
1611
|
+
|
|
1612
|
+
- name: Build
|
|
1613
|
+
run: ${getRunCommand(packageManager, "build")}
|
|
1614
|
+
|
|
1615
|
+
- name: Create GitHub Release
|
|
1616
|
+
uses: softprops/action-gh-release@v1
|
|
1617
|
+
with:
|
|
1618
|
+
generate_release_notes: true
|
|
1619
|
+
`;
|
|
1620
|
+
}
|
|
1621
|
+
async function installCICD(projectPath, config, options) {
|
|
1622
|
+
const result = {
|
|
1623
|
+
created: [],
|
|
1624
|
+
skipped: [],
|
|
1625
|
+
errors: []
|
|
1626
|
+
};
|
|
1627
|
+
if (!config.enabled) {
|
|
1628
|
+
return result;
|
|
1629
|
+
}
|
|
1630
|
+
const workflowsDir = joinPath(projectPath, ".github", "workflows");
|
|
1631
|
+
try {
|
|
1632
|
+
await ensureDir(workflowsDir);
|
|
1633
|
+
if (config.ci) {
|
|
1634
|
+
const ciPath = joinPath(workflowsDir, "ci.yml");
|
|
1635
|
+
if (!await pathExists(ciPath) || options?.overwrite) {
|
|
1636
|
+
const content = generateCIWorkflow(config);
|
|
1637
|
+
await writeFile(ciPath, content);
|
|
1638
|
+
result.created.push("ci.yml");
|
|
1639
|
+
} else {
|
|
1640
|
+
result.skipped.push("ci.yml");
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
if (config.cd) {
|
|
1644
|
+
const releasePath = joinPath(workflowsDir, "release.yml");
|
|
1645
|
+
if (!await pathExists(releasePath) || options?.overwrite) {
|
|
1646
|
+
const content = generateReleaseWorkflow(config);
|
|
1647
|
+
await writeFile(releasePath, content);
|
|
1648
|
+
result.created.push("release.yml");
|
|
1649
|
+
} else {
|
|
1650
|
+
result.skipped.push("release.yml");
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
} catch (error) {
|
|
1654
|
+
result.errors.push(error instanceof Error ? error.message : String(error));
|
|
1655
|
+
}
|
|
1656
|
+
return result;
|
|
1657
|
+
}
|
|
1658
|
+
async function installCICDWithSpinner(projectPath, config, options) {
|
|
1659
|
+
return withSpinner(
|
|
1660
|
+
"Installing GitHub Actions workflows...",
|
|
1661
|
+
() => installCICD(projectPath, config, options),
|
|
1662
|
+
{
|
|
1663
|
+
successText: "Installed GitHub Actions workflows"
|
|
1664
|
+
}
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1330
1668
|
// src/lib/code-style/index.ts
|
|
1331
1669
|
init_esm_shims();
|
|
1332
1670
|
|
|
1333
1671
|
// src/lib/code-style/installer.ts
|
|
1334
1672
|
init_esm_shims();
|
|
1335
|
-
import
|
|
1673
|
+
import path3 from "path";
|
|
1336
1674
|
import fse from "fs-extra";
|
|
1337
1675
|
|
|
1338
1676
|
// src/constants/code-style-defaults.ts
|
|
@@ -1703,107 +2041,7 @@ var colors = {
|
|
|
1703
2041
|
underline: chalk.underline
|
|
1704
2042
|
};
|
|
1705
2043
|
|
|
1706
|
-
// src/lib/
|
|
1707
|
-
init_esm_shims();
|
|
1708
|
-
import ora from "ora";
|
|
1709
|
-
import pc from "picocolors";
|
|
1710
|
-
var SpinnerManager = class {
|
|
1711
|
-
spinner = null;
|
|
1712
|
-
silent = false;
|
|
1713
|
-
configure(options) {
|
|
1714
|
-
if (options.silent !== void 0) this.silent = options.silent;
|
|
1715
|
-
}
|
|
1716
|
-
/**
|
|
1717
|
-
* Start a spinner with a message
|
|
1718
|
-
*/
|
|
1719
|
-
start(text, options) {
|
|
1720
|
-
if (this.silent) return null;
|
|
1721
|
-
this.stop();
|
|
1722
|
-
this.spinner = ora({
|
|
1723
|
-
text,
|
|
1724
|
-
color: options?.color || "cyan",
|
|
1725
|
-
spinner: "dots"
|
|
1726
|
-
}).start();
|
|
1727
|
-
return this.spinner;
|
|
1728
|
-
}
|
|
1729
|
-
/**
|
|
1730
|
-
* Update spinner text
|
|
1731
|
-
*/
|
|
1732
|
-
text(text) {
|
|
1733
|
-
if (this.spinner) {
|
|
1734
|
-
this.spinner.text = text;
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
/**
|
|
1738
|
-
* Stop spinner with success message
|
|
1739
|
-
*/
|
|
1740
|
-
succeed(text) {
|
|
1741
|
-
if (this.spinner) {
|
|
1742
|
-
this.spinner.succeed(text);
|
|
1743
|
-
this.spinner = null;
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
/**
|
|
1747
|
-
* Stop spinner with failure message
|
|
1748
|
-
*/
|
|
1749
|
-
fail(text) {
|
|
1750
|
-
if (this.spinner) {
|
|
1751
|
-
this.spinner.fail(text);
|
|
1752
|
-
this.spinner = null;
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
/**
|
|
1756
|
-
* Stop spinner with warning message
|
|
1757
|
-
*/
|
|
1758
|
-
warn(text) {
|
|
1759
|
-
if (this.spinner) {
|
|
1760
|
-
this.spinner.warn(text);
|
|
1761
|
-
this.spinner = null;
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
/**
|
|
1765
|
-
* Stop spinner with info message
|
|
1766
|
-
*/
|
|
1767
|
-
info(text) {
|
|
1768
|
-
if (this.spinner) {
|
|
1769
|
-
this.spinner.info(text);
|
|
1770
|
-
this.spinner = null;
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
/**
|
|
1774
|
-
* Stop spinner without message
|
|
1775
|
-
*/
|
|
1776
|
-
stop() {
|
|
1777
|
-
if (this.spinner) {
|
|
1778
|
-
this.spinner.stop();
|
|
1779
|
-
this.spinner = null;
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
/**
|
|
1783
|
-
* Check if spinner is running
|
|
1784
|
-
*/
|
|
1785
|
-
isRunning() {
|
|
1786
|
-
return this.spinner?.isSpinning ?? false;
|
|
1787
|
-
}
|
|
1788
|
-
};
|
|
1789
|
-
var spinner = new SpinnerManager();
|
|
1790
|
-
async function withSpinner(text, operation, options) {
|
|
1791
|
-
if (options?.silent) {
|
|
1792
|
-
return operation();
|
|
1793
|
-
}
|
|
1794
|
-
spinner.start(text);
|
|
1795
|
-
try {
|
|
1796
|
-
const result = await operation();
|
|
1797
|
-
spinner.succeed(options?.successText || text);
|
|
1798
|
-
return result;
|
|
1799
|
-
} catch (error) {
|
|
1800
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1801
|
-
spinner.fail(options?.failText || `${text} - ${pc.red(errorMessage)}`);
|
|
1802
|
-
throw error;
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
// src/lib/code-style/generator.ts
|
|
2044
|
+
// src/lib/code-style/generator.ts
|
|
1807
2045
|
init_esm_shims();
|
|
1808
2046
|
function generateEditorConfig(options) {
|
|
1809
2047
|
const indent = options.indentStyle === "tab" ? "indent_style = tab" : "indent_style = space";
|
|
@@ -1983,7 +2221,7 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
1983
2221
|
spinner.start("Installing code style configurations...");
|
|
1984
2222
|
if (config.editorconfig) {
|
|
1985
2223
|
const filename = CODE_STYLE_FILES.editorconfig;
|
|
1986
|
-
const destPath =
|
|
2224
|
+
const destPath = path3.join(targetPath, filename);
|
|
1987
2225
|
try {
|
|
1988
2226
|
if (await fse.pathExists(destPath) && !options?.overwrite) {
|
|
1989
2227
|
result.skipped.push(filename);
|
|
@@ -2000,7 +2238,7 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
2000
2238
|
}
|
|
2001
2239
|
if (config.biome) {
|
|
2002
2240
|
const filename = CODE_STYLE_FILES.biome;
|
|
2003
|
-
const destPath =
|
|
2241
|
+
const destPath = path3.join(targetPath, filename);
|
|
2004
2242
|
try {
|
|
2005
2243
|
if (await fse.pathExists(destPath) && !options?.overwrite) {
|
|
2006
2244
|
result.skipped.push(filename);
|
|
@@ -2017,7 +2255,7 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
2017
2255
|
}
|
|
2018
2256
|
if (config.prettier) {
|
|
2019
2257
|
const filename = CODE_STYLE_FILES.prettier;
|
|
2020
|
-
const destPath =
|
|
2258
|
+
const destPath = path3.join(targetPath, filename);
|
|
2021
2259
|
try {
|
|
2022
2260
|
if (await fse.pathExists(destPath) && !options?.overwrite) {
|
|
2023
2261
|
result.skipped.push(filename);
|
|
@@ -2027,7 +2265,7 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
2027
2265
|
await fse.writeFile(destPath, content, "utf-8");
|
|
2028
2266
|
result.installed.push(filename);
|
|
2029
2267
|
}
|
|
2030
|
-
const ignoreDest =
|
|
2268
|
+
const ignoreDest = path3.join(targetPath, PRETTIER_IGNORE);
|
|
2031
2269
|
if (!await fse.pathExists(ignoreDest) || options?.overwrite) {
|
|
2032
2270
|
const ignoreContent = generatePrettierIgnore();
|
|
2033
2271
|
await fse.writeFile(ignoreDest, ignoreContent, "utf-8");
|
|
@@ -2040,7 +2278,7 @@ async function installCodeStyle(targetPath, config, options) {
|
|
|
2040
2278
|
}
|
|
2041
2279
|
if (config.commitlint) {
|
|
2042
2280
|
const filename = CODE_STYLE_FILES.commitlint;
|
|
2043
|
-
const destPath =
|
|
2281
|
+
const destPath = path3.join(targetPath, filename);
|
|
2044
2282
|
try {
|
|
2045
2283
|
if (await fse.pathExists(destPath) && !options?.overwrite) {
|
|
2046
2284
|
result.skipped.push(filename);
|
|
@@ -2118,6 +2356,214 @@ function showCodeStyleInstructions(config) {
|
|
|
2118
2356
|
}
|
|
2119
2357
|
}
|
|
2120
2358
|
|
|
2359
|
+
// src/lib/code-style/vscode-installer.ts
|
|
2360
|
+
init_esm_shims();
|
|
2361
|
+
init_fs();
|
|
2362
|
+
function generateVSCodeSettings(config) {
|
|
2363
|
+
const settings = {};
|
|
2364
|
+
if (config.biome) {
|
|
2365
|
+
settings["editor.defaultFormatter"] = "biomejs.biome";
|
|
2366
|
+
settings["editor.formatOnSave"] = true;
|
|
2367
|
+
settings["[javascript]"] = {
|
|
2368
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2369
|
+
};
|
|
2370
|
+
settings["[javascriptreact]"] = {
|
|
2371
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2372
|
+
};
|
|
2373
|
+
settings["[typescript]"] = {
|
|
2374
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2375
|
+
};
|
|
2376
|
+
settings["[typescriptreact]"] = {
|
|
2377
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2378
|
+
};
|
|
2379
|
+
settings["[json]"] = {
|
|
2380
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2381
|
+
};
|
|
2382
|
+
settings["[jsonc]"] = {
|
|
2383
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
2384
|
+
};
|
|
2385
|
+
settings["eslint.enable"] = false;
|
|
2386
|
+
settings["biome.enabled"] = true;
|
|
2387
|
+
settings["biome.lintOnSave"] = true;
|
|
2388
|
+
}
|
|
2389
|
+
if (config.prettier && !config.biome) {
|
|
2390
|
+
settings["editor.defaultFormatter"] = "esbenp.prettier-vscode";
|
|
2391
|
+
settings["editor.formatOnSave"] = true;
|
|
2392
|
+
settings["[javascript]"] = {
|
|
2393
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2394
|
+
};
|
|
2395
|
+
settings["[javascriptreact]"] = {
|
|
2396
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2397
|
+
};
|
|
2398
|
+
settings["[typescript]"] = {
|
|
2399
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2400
|
+
};
|
|
2401
|
+
settings["[typescriptreact]"] = {
|
|
2402
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2403
|
+
};
|
|
2404
|
+
settings["[json]"] = {
|
|
2405
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2406
|
+
};
|
|
2407
|
+
settings["[markdown]"] = {
|
|
2408
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2411
|
+
if (config.editorconfig) {
|
|
2412
|
+
settings["editor.detectIndentation"] = false;
|
|
2413
|
+
}
|
|
2414
|
+
return settings;
|
|
2415
|
+
}
|
|
2416
|
+
function generateVSCodeExtensions(config) {
|
|
2417
|
+
const recommendations = [];
|
|
2418
|
+
if (config.biome) {
|
|
2419
|
+
recommendations.push("biomejs.biome");
|
|
2420
|
+
}
|
|
2421
|
+
if (config.prettier && !config.biome) {
|
|
2422
|
+
recommendations.push("esbenp.prettier-vscode");
|
|
2423
|
+
}
|
|
2424
|
+
if (config.editorconfig) {
|
|
2425
|
+
recommendations.push("EditorConfig.EditorConfig");
|
|
2426
|
+
}
|
|
2427
|
+
recommendations.push("dbaeumer.vscode-eslint");
|
|
2428
|
+
return { recommendations };
|
|
2429
|
+
}
|
|
2430
|
+
async function installVSCodeSettings(projectPath, config, options) {
|
|
2431
|
+
if (!config.enabled) {
|
|
2432
|
+
return {
|
|
2433
|
+
created: false,
|
|
2434
|
+
updated: false,
|
|
2435
|
+
skipped: true,
|
|
2436
|
+
path: ""
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
const vscodeDir = joinPath(projectPath, ".vscode");
|
|
2440
|
+
const settingsPath = joinPath(vscodeDir, "settings.json");
|
|
2441
|
+
try {
|
|
2442
|
+
const newSettings = generateVSCodeSettings(config);
|
|
2443
|
+
const exists = await pathExists(settingsPath);
|
|
2444
|
+
if (exists) {
|
|
2445
|
+
if (options?.merge) {
|
|
2446
|
+
const existingContent = await readFile(settingsPath);
|
|
2447
|
+
let existingSettings = {};
|
|
2448
|
+
try {
|
|
2449
|
+
existingSettings = JSON.parse(existingContent);
|
|
2450
|
+
} catch {
|
|
2451
|
+
logger.warn("Could not parse existing settings.json, will overwrite");
|
|
2452
|
+
}
|
|
2453
|
+
const mergedSettings = { ...existingSettings, ...newSettings };
|
|
2454
|
+
await writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
|
|
2455
|
+
return {
|
|
2456
|
+
created: false,
|
|
2457
|
+
updated: true,
|
|
2458
|
+
skipped: false,
|
|
2459
|
+
path: settingsPath
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
if (!options?.overwrite) {
|
|
2463
|
+
return {
|
|
2464
|
+
created: false,
|
|
2465
|
+
updated: false,
|
|
2466
|
+
skipped: true,
|
|
2467
|
+
path: settingsPath
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
await ensureDir(vscodeDir);
|
|
2472
|
+
await writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
2473
|
+
return {
|
|
2474
|
+
created: !exists,
|
|
2475
|
+
updated: exists,
|
|
2476
|
+
skipped: false,
|
|
2477
|
+
path: settingsPath
|
|
2478
|
+
};
|
|
2479
|
+
} catch (error) {
|
|
2480
|
+
return {
|
|
2481
|
+
created: false,
|
|
2482
|
+
updated: false,
|
|
2483
|
+
skipped: false,
|
|
2484
|
+
path: settingsPath,
|
|
2485
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
async function installVSCodeExtensions(projectPath, config, options) {
|
|
2490
|
+
if (!config.enabled) {
|
|
2491
|
+
return {
|
|
2492
|
+
created: false,
|
|
2493
|
+
updated: false,
|
|
2494
|
+
skipped: true,
|
|
2495
|
+
path: ""
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
const vscodeDir = joinPath(projectPath, ".vscode");
|
|
2499
|
+
const extensionsPath = joinPath(vscodeDir, "extensions.json");
|
|
2500
|
+
try {
|
|
2501
|
+
const newExtensions = generateVSCodeExtensions(config);
|
|
2502
|
+
const exists = await pathExists(extensionsPath);
|
|
2503
|
+
if (exists) {
|
|
2504
|
+
if (options?.merge) {
|
|
2505
|
+
const existingContent = await readFile(extensionsPath);
|
|
2506
|
+
let existingExtensions = { recommendations: [] };
|
|
2507
|
+
try {
|
|
2508
|
+
existingExtensions = JSON.parse(existingContent);
|
|
2509
|
+
} catch {
|
|
2510
|
+
logger.warn("Could not parse existing extensions.json, will overwrite");
|
|
2511
|
+
}
|
|
2512
|
+
const mergedRecommendations = [
|
|
2513
|
+
.../* @__PURE__ */ new Set([
|
|
2514
|
+
...existingExtensions.recommendations || [],
|
|
2515
|
+
...newExtensions.recommendations
|
|
2516
|
+
])
|
|
2517
|
+
];
|
|
2518
|
+
await writeFile(
|
|
2519
|
+
extensionsPath,
|
|
2520
|
+
JSON.stringify({ recommendations: mergedRecommendations }, null, 2)
|
|
2521
|
+
);
|
|
2522
|
+
return {
|
|
2523
|
+
created: false,
|
|
2524
|
+
updated: true,
|
|
2525
|
+
skipped: false,
|
|
2526
|
+
path: extensionsPath
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
if (!options?.overwrite) {
|
|
2530
|
+
return {
|
|
2531
|
+
created: false,
|
|
2532
|
+
updated: false,
|
|
2533
|
+
skipped: true,
|
|
2534
|
+
path: extensionsPath
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
await ensureDir(vscodeDir);
|
|
2539
|
+
await writeFile(extensionsPath, JSON.stringify(newExtensions, null, 2));
|
|
2540
|
+
return {
|
|
2541
|
+
created: !exists,
|
|
2542
|
+
updated: exists,
|
|
2543
|
+
skipped: false,
|
|
2544
|
+
path: extensionsPath
|
|
2545
|
+
};
|
|
2546
|
+
} catch (error) {
|
|
2547
|
+
return {
|
|
2548
|
+
created: false,
|
|
2549
|
+
updated: false,
|
|
2550
|
+
skipped: false,
|
|
2551
|
+
path: extensionsPath,
|
|
2552
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
async function installVSCodeConfig(projectPath, config, options) {
|
|
2557
|
+
const [settingsResult, extensionsResult] = await Promise.all([
|
|
2558
|
+
installVSCodeSettings(projectPath, config, options),
|
|
2559
|
+
installVSCodeExtensions(projectPath, config, options)
|
|
2560
|
+
]);
|
|
2561
|
+
return {
|
|
2562
|
+
settings: settingsResult,
|
|
2563
|
+
extensions: extensionsResult
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2121
2567
|
// src/lib/config/index.ts
|
|
2122
2568
|
init_esm_shims();
|
|
2123
2569
|
|
|
@@ -2782,6 +3228,171 @@ function formatManualInstallInstructions(report) {
|
|
|
2782
3228
|
return lines;
|
|
2783
3229
|
}
|
|
2784
3230
|
|
|
3231
|
+
// src/lib/git-hooks/index.ts
|
|
3232
|
+
init_esm_shims();
|
|
3233
|
+
|
|
3234
|
+
// src/lib/git-hooks/husky-installer.ts
|
|
3235
|
+
init_esm_shims();
|
|
3236
|
+
init_fs();
|
|
3237
|
+
function generateCommitMsgHook() {
|
|
3238
|
+
return `#!/usr/bin/env sh
|
|
3239
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3240
|
+
|
|
3241
|
+
npx --no -- commitlint --edit "\${1}"
|
|
3242
|
+
`;
|
|
3243
|
+
}
|
|
3244
|
+
function generatePreCommitHook(lintCommand) {
|
|
3245
|
+
const command = lintCommand || "pnpm lint-staged";
|
|
3246
|
+
return `#!/usr/bin/env sh
|
|
3247
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3248
|
+
|
|
3249
|
+
${command}
|
|
3250
|
+
`;
|
|
3251
|
+
}
|
|
3252
|
+
function generatePrePushHook(testCommand) {
|
|
3253
|
+
const command = testCommand || "pnpm test";
|
|
3254
|
+
return `#!/usr/bin/env sh
|
|
3255
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3256
|
+
|
|
3257
|
+
${command}
|
|
3258
|
+
`;
|
|
3259
|
+
}
|
|
3260
|
+
function generateHuskyScript() {
|
|
3261
|
+
return `#!/usr/bin/env sh
|
|
3262
|
+
if [ -z "$husky_skip_init" ]; then
|
|
3263
|
+
debug () {
|
|
3264
|
+
if [ "$HUSKY_DEBUG" = "1" ]; then
|
|
3265
|
+
echo "husky (debug) - $1"
|
|
3266
|
+
fi
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
readonly hook_name="$(basename -- "$0")"
|
|
3270
|
+
debug "starting $hook_name..."
|
|
3271
|
+
|
|
3272
|
+
if [ "$HUSKY" = "0" ]; then
|
|
3273
|
+
debug "HUSKY env variable is set to 0, skipping hook"
|
|
3274
|
+
exit 0
|
|
3275
|
+
fi
|
|
3276
|
+
|
|
3277
|
+
if [ -f ~/.huskyrc ]; then
|
|
3278
|
+
debug "sourcing ~/.huskyrc"
|
|
3279
|
+
. ~/.huskyrc
|
|
3280
|
+
fi
|
|
3281
|
+
|
|
3282
|
+
readonly husky_skip_init=1
|
|
3283
|
+
export husky_skip_init
|
|
3284
|
+
sh -e "$0" "$@"
|
|
3285
|
+
exitCode="$?"
|
|
3286
|
+
|
|
3287
|
+
if [ $exitCode != 0 ]; then
|
|
3288
|
+
echo "husky - $hook_name hook exited with code $exitCode (error)"
|
|
3289
|
+
fi
|
|
3290
|
+
|
|
3291
|
+
if [ $exitCode = 127 ]; then
|
|
3292
|
+
echo "husky - command not found in PATH=$PATH"
|
|
3293
|
+
fi
|
|
3294
|
+
|
|
3295
|
+
exit $exitCode
|
|
3296
|
+
fi
|
|
3297
|
+
`;
|
|
3298
|
+
}
|
|
3299
|
+
function generateHuskyGitignore() {
|
|
3300
|
+
return `_
|
|
3301
|
+
`;
|
|
3302
|
+
}
|
|
3303
|
+
async function installHusky(projectPath, config, options) {
|
|
3304
|
+
const result = {
|
|
3305
|
+
created: [],
|
|
3306
|
+
skipped: [],
|
|
3307
|
+
errors: [],
|
|
3308
|
+
initialized: false
|
|
3309
|
+
};
|
|
3310
|
+
const huskyDir = joinPath(projectPath, ".husky");
|
|
3311
|
+
const huskyInternalDir = joinPath(huskyDir, "_");
|
|
3312
|
+
try {
|
|
3313
|
+
await ensureDir(huskyDir);
|
|
3314
|
+
await ensureDir(huskyInternalDir);
|
|
3315
|
+
result.initialized = true;
|
|
3316
|
+
const huskyScriptPath = joinPath(huskyInternalDir, "husky.sh");
|
|
3317
|
+
if (!await pathExists(huskyScriptPath) || options?.overwrite) {
|
|
3318
|
+
await writeFile(huskyScriptPath, generateHuskyScript());
|
|
3319
|
+
await makeExecutable(huskyScriptPath);
|
|
3320
|
+
result.created.push("_/husky.sh");
|
|
3321
|
+
} else {
|
|
3322
|
+
result.skipped.push("_/husky.sh");
|
|
3323
|
+
}
|
|
3324
|
+
const gitignorePath = joinPath(huskyInternalDir, ".gitignore");
|
|
3325
|
+
if (!await pathExists(gitignorePath) || options?.overwrite) {
|
|
3326
|
+
await writeFile(gitignorePath, generateHuskyGitignore());
|
|
3327
|
+
result.created.push("_/.gitignore");
|
|
3328
|
+
}
|
|
3329
|
+
if (config.commitlint) {
|
|
3330
|
+
const commitMsgPath = joinPath(huskyDir, "commit-msg");
|
|
3331
|
+
if (!await pathExists(commitMsgPath) || options?.overwrite) {
|
|
3332
|
+
await writeFile(commitMsgPath, generateCommitMsgHook());
|
|
3333
|
+
await makeExecutable(commitMsgPath);
|
|
3334
|
+
result.created.push("commit-msg");
|
|
3335
|
+
} else {
|
|
3336
|
+
result.skipped.push("commit-msg");
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
if (config.preCommit) {
|
|
3340
|
+
const preCommitPath = joinPath(huskyDir, "pre-commit");
|
|
3341
|
+
if (!await pathExists(preCommitPath) || options?.overwrite) {
|
|
3342
|
+
await writeFile(preCommitPath, generatePreCommitHook(config.lintCommand));
|
|
3343
|
+
await makeExecutable(preCommitPath);
|
|
3344
|
+
result.created.push("pre-commit");
|
|
3345
|
+
} else {
|
|
3346
|
+
result.skipped.push("pre-commit");
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
if (config.prePush) {
|
|
3350
|
+
const prePushPath = joinPath(huskyDir, "pre-push");
|
|
3351
|
+
if (!await pathExists(prePushPath) || options?.overwrite) {
|
|
3352
|
+
await writeFile(prePushPath, generatePrePushHook(config.testCommand));
|
|
3353
|
+
await makeExecutable(prePushPath);
|
|
3354
|
+
result.created.push("pre-push");
|
|
3355
|
+
} else {
|
|
3356
|
+
result.skipped.push("pre-push");
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
} catch (error) {
|
|
3360
|
+
result.errors.push(error instanceof Error ? error.message : String(error));
|
|
3361
|
+
}
|
|
3362
|
+
return result;
|
|
3363
|
+
}
|
|
3364
|
+
async function installHuskyWithSpinner(projectPath, config, options) {
|
|
3365
|
+
return withSpinner(
|
|
3366
|
+
"Installing Husky hooks...",
|
|
3367
|
+
() => installHusky(projectPath, config, options),
|
|
3368
|
+
{
|
|
3369
|
+
successText: "Installed Husky hooks"
|
|
3370
|
+
}
|
|
3371
|
+
);
|
|
3372
|
+
}
|
|
3373
|
+
function deriveHuskyConfigFromCodeStyle(codeStyle) {
|
|
3374
|
+
if (!codeStyle.enabled) {
|
|
3375
|
+
return null;
|
|
3376
|
+
}
|
|
3377
|
+
const commitlintEnabled = codeStyle.commitlint && codeStyle.commitlintOptions?.huskyIntegration;
|
|
3378
|
+
if (!commitlintEnabled) {
|
|
3379
|
+
return null;
|
|
3380
|
+
}
|
|
3381
|
+
let lintCommand;
|
|
3382
|
+
if (codeStyle.biome) {
|
|
3383
|
+
lintCommand = "pnpm biome check --staged";
|
|
3384
|
+
} else if (codeStyle.prettier) {
|
|
3385
|
+
lintCommand = "pnpm lint-staged";
|
|
3386
|
+
}
|
|
3387
|
+
return {
|
|
3388
|
+
commitlint: true,
|
|
3389
|
+
preCommit: !!lintCommand,
|
|
3390
|
+
lintCommand,
|
|
3391
|
+
prePush: false
|
|
3392
|
+
// Don't run tests on push by default
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
|
|
2785
3396
|
// src/lib/hooks/index.ts
|
|
2786
3397
|
init_esm_shims();
|
|
2787
3398
|
|
|
@@ -2985,44 +3596,72 @@ import * as os2 from "os";
|
|
|
2985
3596
|
init_esm_shims();
|
|
2986
3597
|
var MCP_SERVERS = [
|
|
2987
3598
|
// ============================================
|
|
2988
|
-
// DOCUMENTATION
|
|
3599
|
+
// DOCUMENTATION & AI TOOLS
|
|
2989
3600
|
// ============================================
|
|
2990
3601
|
{
|
|
2991
3602
|
id: "context7",
|
|
2992
3603
|
name: "Context7",
|
|
2993
|
-
description: "
|
|
2994
|
-
package: "@
|
|
3604
|
+
description: "Up-to-date documentation lookup for libraries and frameworks",
|
|
3605
|
+
package: "@upstash/context7-mcp",
|
|
2995
3606
|
category: "documentation",
|
|
2996
|
-
requiresConfig: false
|
|
3607
|
+
requiresConfig: false,
|
|
3608
|
+
installInstructions: "API keys provide higher rate limits. Get one at https://context7.com/dashboard"
|
|
3609
|
+
},
|
|
3610
|
+
{
|
|
3611
|
+
id: "perplexity",
|
|
3612
|
+
name: "Perplexity",
|
|
3613
|
+
description: "Web search without leaving the MCP ecosystem via Sonar API",
|
|
3614
|
+
package: "@chatmcp/server-perplexity-ask",
|
|
3615
|
+
category: "search",
|
|
3616
|
+
requiresConfig: true,
|
|
3617
|
+
configFields: [
|
|
3618
|
+
{
|
|
3619
|
+
name: "apiKey",
|
|
3620
|
+
type: "string",
|
|
3621
|
+
required: true,
|
|
3622
|
+
description: "Perplexity/Sonar API Key",
|
|
3623
|
+
envVar: "PERPLEXITY_API_KEY"
|
|
3624
|
+
}
|
|
3625
|
+
],
|
|
3626
|
+
installInstructions: "Get API key at https://www.perplexity.ai/settings/api"
|
|
3627
|
+
},
|
|
3628
|
+
{
|
|
3629
|
+
id: "sequential-thinking",
|
|
3630
|
+
name: "Sequential Thinking",
|
|
3631
|
+
description: "Dynamic problem-solving through structured thinking process",
|
|
3632
|
+
package: "@modelcontextprotocol/server-sequential-thinking",
|
|
3633
|
+
category: "ai",
|
|
3634
|
+
requiresConfig: false,
|
|
3635
|
+
installInstructions: "Helps break down complex problems into manageable steps."
|
|
2997
3636
|
},
|
|
2998
3637
|
// ============================================
|
|
2999
3638
|
// TESTING & BROWSER AUTOMATION
|
|
3000
3639
|
// ============================================
|
|
3001
3640
|
{
|
|
3002
|
-
id: "
|
|
3003
|
-
name: "
|
|
3004
|
-
description: "
|
|
3005
|
-
package: "@
|
|
3641
|
+
id: "puppeteer",
|
|
3642
|
+
name: "Puppeteer",
|
|
3643
|
+
description: "Headless Chrome automation for testing and scraping",
|
|
3644
|
+
package: "@modelcontextprotocol/server-puppeteer",
|
|
3006
3645
|
category: "testing",
|
|
3007
|
-
requiresConfig: false
|
|
3008
|
-
installInstructions: "Requires Chrome/Chromium browser installed on the system."
|
|
3646
|
+
requiresConfig: false
|
|
3009
3647
|
},
|
|
3010
3648
|
{
|
|
3011
3649
|
id: "playwright",
|
|
3012
3650
|
name: "Playwright",
|
|
3013
|
-
description: "
|
|
3014
|
-
package: "@
|
|
3651
|
+
description: "Browser automation via accessibility snapshots, not screenshots",
|
|
3652
|
+
package: "@playwright/mcp",
|
|
3015
3653
|
category: "testing",
|
|
3016
3654
|
requiresConfig: false,
|
|
3017
|
-
installInstructions: "
|
|
3655
|
+
installInstructions: "Requires Node.js 18+. Supports Chrome, Firefox, WebKit."
|
|
3018
3656
|
},
|
|
3019
3657
|
{
|
|
3020
|
-
id: "
|
|
3021
|
-
name: "
|
|
3022
|
-
description: "
|
|
3023
|
-
package: "
|
|
3658
|
+
id: "chrome-devtools",
|
|
3659
|
+
name: "Chrome DevTools",
|
|
3660
|
+
description: "Control and inspect live Chrome browser with DevTools Protocol",
|
|
3661
|
+
package: "chrome-devtools-mcp",
|
|
3024
3662
|
category: "testing",
|
|
3025
|
-
requiresConfig: false
|
|
3663
|
+
requiresConfig: false,
|
|
3664
|
+
installInstructions: "Requires Chrome installed. For Claude Code: claude mcp add chrome-devtools npx chrome-devtools-mcp@latest"
|
|
3026
3665
|
},
|
|
3027
3666
|
// ============================================
|
|
3028
3667
|
// VERSION CONTROL
|
|
@@ -3030,7 +3669,7 @@ var MCP_SERVERS = [
|
|
|
3030
3669
|
{
|
|
3031
3670
|
id: "github",
|
|
3032
3671
|
name: "GitHub",
|
|
3033
|
-
description: "GitHub API integration (issues, PRs, repos)",
|
|
3672
|
+
description: "GitHub API integration (issues, PRs, repos, file operations)",
|
|
3034
3673
|
package: "@modelcontextprotocol/server-github",
|
|
3035
3674
|
category: "version-control",
|
|
3036
3675
|
requiresConfig: true,
|
|
@@ -3043,32 +3682,33 @@ var MCP_SERVERS = [
|
|
|
3043
3682
|
envVar: "GITHUB_TOKEN"
|
|
3044
3683
|
}
|
|
3045
3684
|
],
|
|
3046
|
-
installInstructions: "Create a Personal Access Token at https://github.com/settings/tokens with repo
|
|
3685
|
+
installInstructions: "Create a Personal Access Token at https://github.com/settings/tokens with repo scope."
|
|
3047
3686
|
},
|
|
3048
3687
|
{
|
|
3049
3688
|
id: "gitlab",
|
|
3050
3689
|
name: "GitLab",
|
|
3051
|
-
description: "GitLab API
|
|
3052
|
-
package: "@
|
|
3690
|
+
description: "GitLab API for project management, issues, and merge requests",
|
|
3691
|
+
package: "@modelcontextprotocol/server-gitlab",
|
|
3053
3692
|
category: "version-control",
|
|
3054
3693
|
requiresConfig: true,
|
|
3055
3694
|
configFields: [
|
|
3056
3695
|
{
|
|
3057
3696
|
name: "token",
|
|
3058
3697
|
type: "string",
|
|
3059
|
-
required:
|
|
3698
|
+
required: true,
|
|
3060
3699
|
description: "GitLab Personal Access Token",
|
|
3061
|
-
envVar: "
|
|
3700
|
+
envVar: "GITLAB_PERSONAL_ACCESS_TOKEN"
|
|
3062
3701
|
},
|
|
3063
3702
|
{
|
|
3064
|
-
name: "
|
|
3703
|
+
name: "apiUrl",
|
|
3065
3704
|
type: "string",
|
|
3066
3705
|
required: false,
|
|
3067
|
-
description: "GitLab
|
|
3068
|
-
|
|
3706
|
+
description: "GitLab API URL (default: https://gitlab.com/api/v4)",
|
|
3707
|
+
envVar: "GITLAB_API_URL",
|
|
3708
|
+
default: "https://gitlab.com/api/v4"
|
|
3069
3709
|
}
|
|
3070
3710
|
],
|
|
3071
|
-
installInstructions: "Create
|
|
3711
|
+
installInstructions: "Create PAT at GitLab User Settings > Access Tokens with api, read_repository, write_repository scopes."
|
|
3072
3712
|
},
|
|
3073
3713
|
// ============================================
|
|
3074
3714
|
// DATABASES
|
|
@@ -3076,7 +3716,7 @@ var MCP_SERVERS = [
|
|
|
3076
3716
|
{
|
|
3077
3717
|
id: "postgres",
|
|
3078
3718
|
name: "PostgreSQL",
|
|
3079
|
-
description: "
|
|
3719
|
+
description: "Read-only PostgreSQL database access with schema inspection",
|
|
3080
3720
|
package: "@modelcontextprotocol/server-postgres",
|
|
3081
3721
|
category: "database",
|
|
3082
3722
|
requiresConfig: true,
|
|
@@ -3092,63 +3732,45 @@ var MCP_SERVERS = [
|
|
|
3092
3732
|
installInstructions: "Connection string format: postgresql://user:password@host:port/database"
|
|
3093
3733
|
},
|
|
3094
3734
|
{
|
|
3095
|
-
id: "
|
|
3096
|
-
name: "
|
|
3097
|
-
description: "
|
|
3098
|
-
package: "@
|
|
3099
|
-
category: "database",
|
|
3100
|
-
requiresConfig: true,
|
|
3101
|
-
configFields: [
|
|
3102
|
-
{
|
|
3103
|
-
name: "apiKey",
|
|
3104
|
-
type: "string",
|
|
3105
|
-
required: false,
|
|
3106
|
-
description: "Neon API Key",
|
|
3107
|
-
envVar: "NEON_API_KEY"
|
|
3108
|
-
}
|
|
3109
|
-
],
|
|
3110
|
-
installInstructions: "Get your API key from https://console.neon.tech/app/settings/api-keys"
|
|
3111
|
-
},
|
|
3112
|
-
{
|
|
3113
|
-
id: "mongodb",
|
|
3114
|
-
name: "MongoDB",
|
|
3115
|
-
description: "MongoDB document database access",
|
|
3116
|
-
package: "@anthropic/mongodb-mcp",
|
|
3735
|
+
id: "mysql",
|
|
3736
|
+
name: "MySQL",
|
|
3737
|
+
description: "Read-only MySQL/MariaDB database access",
|
|
3738
|
+
package: "@modelcontextprotocol/server-mysql",
|
|
3117
3739
|
category: "database",
|
|
3118
3740
|
requiresConfig: true,
|
|
3119
3741
|
configFields: [
|
|
3120
3742
|
{
|
|
3121
3743
|
name: "connectionString",
|
|
3122
3744
|
type: "string",
|
|
3123
|
-
required:
|
|
3124
|
-
description: "
|
|
3125
|
-
envVar: "
|
|
3745
|
+
required: true,
|
|
3746
|
+
description: "MySQL connection URL",
|
|
3747
|
+
envVar: "MYSQL_URL"
|
|
3126
3748
|
}
|
|
3127
3749
|
],
|
|
3128
|
-
installInstructions: "Connection
|
|
3750
|
+
installInstructions: "Connection format: mysql://user:password@host:port/database"
|
|
3129
3751
|
},
|
|
3130
3752
|
{
|
|
3131
|
-
id: "
|
|
3132
|
-
name: "
|
|
3133
|
-
description: "
|
|
3134
|
-
package: "@
|
|
3753
|
+
id: "neon",
|
|
3754
|
+
name: "Neon",
|
|
3755
|
+
description: "Neon serverless PostgreSQL with branch management",
|
|
3756
|
+
package: "@neondatabase/mcp-server-neon",
|
|
3135
3757
|
category: "database",
|
|
3136
3758
|
requiresConfig: true,
|
|
3137
3759
|
configFields: [
|
|
3138
3760
|
{
|
|
3139
|
-
name: "
|
|
3761
|
+
name: "apiKey",
|
|
3140
3762
|
type: "string",
|
|
3141
3763
|
required: false,
|
|
3142
|
-
description: "
|
|
3143
|
-
envVar: "
|
|
3764
|
+
description: "Neon API Key",
|
|
3765
|
+
envVar: "NEON_API_KEY"
|
|
3144
3766
|
}
|
|
3145
3767
|
],
|
|
3146
|
-
installInstructions: "
|
|
3768
|
+
installInstructions: "Get your API key from https://console.neon.tech/app/settings/api-keys"
|
|
3147
3769
|
},
|
|
3148
3770
|
{
|
|
3149
3771
|
id: "sqlite",
|
|
3150
3772
|
name: "SQLite",
|
|
3151
|
-
description: "SQLite
|
|
3773
|
+
description: "SQLite database interaction and business intelligence",
|
|
3152
3774
|
package: "@modelcontextprotocol/server-sqlite",
|
|
3153
3775
|
category: "database",
|
|
3154
3776
|
requiresConfig: true,
|
|
@@ -3165,27 +3787,20 @@ var MCP_SERVERS = [
|
|
|
3165
3787
|
{
|
|
3166
3788
|
id: "supabase",
|
|
3167
3789
|
name: "Supabase",
|
|
3168
|
-
description: "Supabase
|
|
3169
|
-
package: "@supabase/mcp-server",
|
|
3790
|
+
description: "Supabase projects, database, Edge Functions, and type generation",
|
|
3791
|
+
package: "@supabase/mcp-server-supabase",
|
|
3170
3792
|
category: "database",
|
|
3171
3793
|
requiresConfig: true,
|
|
3172
3794
|
configFields: [
|
|
3173
3795
|
{
|
|
3174
|
-
name: "
|
|
3175
|
-
type: "string",
|
|
3176
|
-
required: false,
|
|
3177
|
-
description: "Supabase project URL",
|
|
3178
|
-
envVar: "SUPABASE_URL"
|
|
3179
|
-
},
|
|
3180
|
-
{
|
|
3181
|
-
name: "anonKey",
|
|
3796
|
+
name: "accessToken",
|
|
3182
3797
|
type: "string",
|
|
3183
3798
|
required: false,
|
|
3184
|
-
description: "Supabase
|
|
3185
|
-
envVar: "
|
|
3799
|
+
description: "Supabase Personal Access Token",
|
|
3800
|
+
envVar: "SUPABASE_ACCESS_TOKEN"
|
|
3186
3801
|
}
|
|
3187
3802
|
],
|
|
3188
|
-
installInstructions: "
|
|
3803
|
+
installInstructions: "Get your access token from https://supabase.com/dashboard/account/tokens"
|
|
3189
3804
|
},
|
|
3190
3805
|
// ============================================
|
|
3191
3806
|
// CACHE & KEY-VALUE STORES
|
|
@@ -3193,8 +3808,8 @@ var MCP_SERVERS = [
|
|
|
3193
3808
|
{
|
|
3194
3809
|
id: "redis",
|
|
3195
3810
|
name: "Redis",
|
|
3196
|
-
description: "Redis
|
|
3197
|
-
package: "@
|
|
3811
|
+
description: "Redis key-value store operations (set, get, delete, list)",
|
|
3812
|
+
package: "@modelcontextprotocol/server-redis",
|
|
3198
3813
|
category: "cache",
|
|
3199
3814
|
requiresConfig: true,
|
|
3200
3815
|
configFields: [
|
|
@@ -3207,29 +3822,29 @@ var MCP_SERVERS = [
|
|
|
3207
3822
|
default: "redis://localhost:6379"
|
|
3208
3823
|
}
|
|
3209
3824
|
],
|
|
3210
|
-
installInstructions: "
|
|
3825
|
+
installInstructions: "Pass Redis URL as argument: npx @modelcontextprotocol/server-redis redis://localhost:6379"
|
|
3211
3826
|
},
|
|
3212
3827
|
{
|
|
3213
3828
|
id: "upstash",
|
|
3214
3829
|
name: "Upstash",
|
|
3215
|
-
description: "Upstash
|
|
3830
|
+
description: "Upstash Redis database management and commands",
|
|
3216
3831
|
package: "@upstash/mcp-server",
|
|
3217
3832
|
category: "cache",
|
|
3218
3833
|
requiresConfig: true,
|
|
3219
3834
|
configFields: [
|
|
3220
3835
|
{
|
|
3221
|
-
name: "
|
|
3836
|
+
name: "email",
|
|
3222
3837
|
type: "string",
|
|
3223
3838
|
required: false,
|
|
3224
|
-
description: "Upstash
|
|
3225
|
-
envVar: "
|
|
3839
|
+
description: "Upstash account email",
|
|
3840
|
+
envVar: "UPSTASH_EMAIL"
|
|
3226
3841
|
},
|
|
3227
3842
|
{
|
|
3228
|
-
name: "
|
|
3843
|
+
name: "apiKey",
|
|
3229
3844
|
type: "string",
|
|
3230
3845
|
required: false,
|
|
3231
|
-
description: "Upstash
|
|
3232
|
-
envVar: "
|
|
3846
|
+
description: "Upstash API Key",
|
|
3847
|
+
envVar: "UPSTASH_API_KEY"
|
|
3233
3848
|
}
|
|
3234
3849
|
],
|
|
3235
3850
|
installInstructions: "Get credentials from https://console.upstash.com"
|
|
@@ -3237,57 +3852,14 @@ var MCP_SERVERS = [
|
|
|
3237
3852
|
// ============================================
|
|
3238
3853
|
// DEPLOYMENT & INFRASTRUCTURE
|
|
3239
3854
|
// ============================================
|
|
3240
|
-
{
|
|
3241
|
-
id: "vercel",
|
|
3242
|
-
name: "Vercel",
|
|
3243
|
-
description: "Vercel deployment and project management",
|
|
3244
|
-
package: "@vercel/mcp",
|
|
3245
|
-
category: "deployment",
|
|
3246
|
-
requiresConfig: true,
|
|
3247
|
-
configFields: [
|
|
3248
|
-
{
|
|
3249
|
-
name: "token",
|
|
3250
|
-
type: "string",
|
|
3251
|
-
required: false,
|
|
3252
|
-
description: "Vercel Access Token",
|
|
3253
|
-
envVar: "VERCEL_TOKEN"
|
|
3254
|
-
}
|
|
3255
|
-
],
|
|
3256
|
-
installInstructions: "Create an access token at https://vercel.com/account/tokens"
|
|
3257
|
-
},
|
|
3258
|
-
{
|
|
3259
|
-
id: "netlify",
|
|
3260
|
-
name: "Netlify",
|
|
3261
|
-
description: "Netlify deployment and site management",
|
|
3262
|
-
package: "@anthropic/netlify-mcp",
|
|
3263
|
-
category: "deployment",
|
|
3264
|
-
requiresConfig: true,
|
|
3265
|
-
configFields: [
|
|
3266
|
-
{
|
|
3267
|
-
name: "token",
|
|
3268
|
-
type: "string",
|
|
3269
|
-
required: false,
|
|
3270
|
-
description: "Netlify Personal Access Token",
|
|
3271
|
-
envVar: "NETLIFY_TOKEN"
|
|
3272
|
-
}
|
|
3273
|
-
],
|
|
3274
|
-
installInstructions: "Create a token at https://app.netlify.com/user/applications#personal-access-tokens"
|
|
3275
|
-
},
|
|
3276
3855
|
{
|
|
3277
3856
|
id: "cloudflare",
|
|
3278
3857
|
name: "Cloudflare",
|
|
3279
|
-
description: "Cloudflare Workers,
|
|
3280
|
-
package: "@cloudflare/mcp-server",
|
|
3858
|
+
description: "Cloudflare Workers, D1, KV, R2, and DNS management",
|
|
3859
|
+
package: "@cloudflare/mcp-server-cloudflare",
|
|
3281
3860
|
category: "deployment",
|
|
3282
3861
|
requiresConfig: true,
|
|
3283
3862
|
configFields: [
|
|
3284
|
-
{
|
|
3285
|
-
name: "apiToken",
|
|
3286
|
-
type: "string",
|
|
3287
|
-
required: false,
|
|
3288
|
-
description: "Cloudflare API Token",
|
|
3289
|
-
envVar: "CLOUDFLARE_API_TOKEN"
|
|
3290
|
-
},
|
|
3291
3863
|
{
|
|
3292
3864
|
name: "accountId",
|
|
3293
3865
|
type: "string",
|
|
@@ -3296,437 +3868,246 @@ var MCP_SERVERS = [
|
|
|
3296
3868
|
envVar: "CLOUDFLARE_ACCOUNT_ID"
|
|
3297
3869
|
}
|
|
3298
3870
|
],
|
|
3299
|
-
installInstructions: "
|
|
3300
|
-
},
|
|
3301
|
-
{
|
|
3302
|
-
id: "aws",
|
|
3303
|
-
name: "AWS",
|
|
3304
|
-
description: "Amazon Web Services integration",
|
|
3305
|
-
package: "@anthropic/aws-mcp",
|
|
3306
|
-
category: "infrastructure",
|
|
3307
|
-
requiresConfig: true,
|
|
3308
|
-
configFields: [
|
|
3309
|
-
{
|
|
3310
|
-
name: "accessKeyId",
|
|
3311
|
-
type: "string",
|
|
3312
|
-
required: false,
|
|
3313
|
-
description: "AWS Access Key ID",
|
|
3314
|
-
envVar: "AWS_ACCESS_KEY_ID"
|
|
3315
|
-
},
|
|
3316
|
-
{
|
|
3317
|
-
name: "secretAccessKey",
|
|
3318
|
-
type: "string",
|
|
3319
|
-
required: false,
|
|
3320
|
-
description: "AWS Secret Access Key",
|
|
3321
|
-
envVar: "AWS_SECRET_ACCESS_KEY"
|
|
3322
|
-
},
|
|
3323
|
-
{
|
|
3324
|
-
name: "region",
|
|
3325
|
-
type: "string",
|
|
3326
|
-
required: false,
|
|
3327
|
-
description: "AWS Region",
|
|
3328
|
-
envVar: "AWS_REGION",
|
|
3329
|
-
default: "us-east-1"
|
|
3330
|
-
}
|
|
3331
|
-
],
|
|
3332
|
-
installInstructions: "Create credentials in AWS IAM Console with appropriate permissions."
|
|
3871
|
+
installInstructions: "Run: npx @cloudflare/mcp-server-cloudflare init"
|
|
3333
3872
|
},
|
|
3334
3873
|
{
|
|
3335
|
-
id: "
|
|
3336
|
-
name: "
|
|
3337
|
-
description: "
|
|
3338
|
-
package: "
|
|
3339
|
-
category: "
|
|
3340
|
-
requiresConfig: false,
|
|
3341
|
-
installInstructions: "Requires Docker Desktop or Docker Engine installed."
|
|
3342
|
-
},
|
|
3343
|
-
{
|
|
3344
|
-
id: "kubernetes",
|
|
3345
|
-
name: "Kubernetes",
|
|
3346
|
-
description: "Kubernetes cluster management",
|
|
3347
|
-
package: "@anthropic/kubernetes-mcp",
|
|
3348
|
-
category: "infrastructure",
|
|
3874
|
+
id: "vercel",
|
|
3875
|
+
name: "Vercel",
|
|
3876
|
+
description: "Vercel deployments, DNS records, and project management",
|
|
3877
|
+
package: "vercel-mcp",
|
|
3878
|
+
category: "deployment",
|
|
3349
3879
|
requiresConfig: true,
|
|
3350
3880
|
configFields: [
|
|
3351
3881
|
{
|
|
3352
|
-
name: "
|
|
3353
|
-
type: "string",
|
|
3354
|
-
required: false,
|
|
3355
|
-
description: "Path to kubeconfig file",
|
|
3356
|
-
default: "~/.kube/config"
|
|
3357
|
-
},
|
|
3358
|
-
{
|
|
3359
|
-
name: "context",
|
|
3882
|
+
name: "apiKey",
|
|
3360
3883
|
type: "string",
|
|
3361
|
-
required:
|
|
3362
|
-
description: "
|
|
3884
|
+
required: true,
|
|
3885
|
+
description: "Vercel API Key",
|
|
3886
|
+
envVar: "VERCEL_API_KEY"
|
|
3363
3887
|
}
|
|
3364
3888
|
],
|
|
3365
|
-
installInstructions: "
|
|
3889
|
+
installInstructions: "Get API key at https://vercel.com/account/tokens"
|
|
3366
3890
|
},
|
|
3367
3891
|
{
|
|
3368
3892
|
id: "filesystem",
|
|
3369
3893
|
name: "Filesystem",
|
|
3370
|
-
description: "
|
|
3894
|
+
description: "Secure file operations with configurable access controls",
|
|
3371
3895
|
package: "@modelcontextprotocol/server-filesystem",
|
|
3372
3896
|
category: "infrastructure",
|
|
3373
3897
|
requiresConfig: false
|
|
3374
3898
|
},
|
|
3375
|
-
// ============================================
|
|
3376
|
-
// PROJECT MANAGEMENT
|
|
3377
|
-
// ============================================
|
|
3378
|
-
{
|
|
3379
|
-
id: "linear",
|
|
3380
|
-
name: "Linear",
|
|
3381
|
-
description: "Linear project management integration",
|
|
3382
|
-
package: "@anthropic/linear-mcp",
|
|
3383
|
-
category: "project-mgmt",
|
|
3384
|
-
requiresConfig: true,
|
|
3385
|
-
configFields: [
|
|
3386
|
-
{
|
|
3387
|
-
name: "apiKey",
|
|
3388
|
-
type: "string",
|
|
3389
|
-
required: false,
|
|
3390
|
-
description: "Linear API Key",
|
|
3391
|
-
envVar: "LINEAR_API_KEY"
|
|
3392
|
-
}
|
|
3393
|
-
],
|
|
3394
|
-
installInstructions: "Create an API key at https://linear.app/settings/api"
|
|
3395
|
-
},
|
|
3396
3899
|
{
|
|
3397
|
-
id: "
|
|
3398
|
-
name: "
|
|
3399
|
-
description: "
|
|
3400
|
-
package: "@
|
|
3401
|
-
category: "
|
|
3402
|
-
requiresConfig:
|
|
3403
|
-
|
|
3404
|
-
{
|
|
3405
|
-
name: "host",
|
|
3406
|
-
type: "string",
|
|
3407
|
-
required: false,
|
|
3408
|
-
description: "Jira instance URL",
|
|
3409
|
-
envVar: "JIRA_HOST"
|
|
3410
|
-
},
|
|
3411
|
-
{
|
|
3412
|
-
name: "email",
|
|
3413
|
-
type: "string",
|
|
3414
|
-
required: false,
|
|
3415
|
-
description: "Jira account email",
|
|
3416
|
-
envVar: "JIRA_EMAIL"
|
|
3417
|
-
},
|
|
3418
|
-
{
|
|
3419
|
-
name: "token",
|
|
3420
|
-
type: "string",
|
|
3421
|
-
required: false,
|
|
3422
|
-
description: "Jira API Token",
|
|
3423
|
-
envVar: "JIRA_TOKEN"
|
|
3424
|
-
}
|
|
3425
|
-
],
|
|
3426
|
-
installInstructions: "Create an API token at https://id.atlassian.com/manage-profile/security/api-tokens"
|
|
3900
|
+
id: "memory",
|
|
3901
|
+
name: "Memory",
|
|
3902
|
+
description: "Knowledge graph-based persistent memory system",
|
|
3903
|
+
package: "@modelcontextprotocol/server-memory",
|
|
3904
|
+
category: "infrastructure",
|
|
3905
|
+
requiresConfig: false,
|
|
3906
|
+
installInstructions: "Memory is stored in memory.jsonl in the server directory by default."
|
|
3427
3907
|
},
|
|
3908
|
+
// ============================================
|
|
3909
|
+
// PROJECT MANAGEMENT & PRODUCTIVITY
|
|
3910
|
+
// ============================================
|
|
3428
3911
|
{
|
|
3429
3912
|
id: "notion",
|
|
3430
3913
|
name: "Notion",
|
|
3431
|
-
description: "Notion
|
|
3432
|
-
package: "@
|
|
3914
|
+
description: "Official Notion API for pages, databases, and workspace",
|
|
3915
|
+
package: "@notionhq/notion-mcp-server",
|
|
3433
3916
|
category: "project-mgmt",
|
|
3434
3917
|
requiresConfig: true,
|
|
3435
3918
|
configFields: [
|
|
3436
3919
|
{
|
|
3437
3920
|
name: "token",
|
|
3438
3921
|
type: "string",
|
|
3439
|
-
required:
|
|
3922
|
+
required: true,
|
|
3440
3923
|
description: "Notion Integration Token",
|
|
3441
3924
|
envVar: "NOTION_TOKEN"
|
|
3442
3925
|
}
|
|
3443
3926
|
],
|
|
3444
|
-
installInstructions: "Create
|
|
3927
|
+
installInstructions: "Create integration at https://www.notion.so/profile/integrations and share pages with it."
|
|
3445
3928
|
},
|
|
3446
3929
|
{
|
|
3447
|
-
id: "
|
|
3448
|
-
name: "
|
|
3449
|
-
description: "
|
|
3450
|
-
package: "
|
|
3930
|
+
id: "obsidian",
|
|
3931
|
+
name: "Obsidian",
|
|
3932
|
+
description: "Read and search Obsidian vaults and Markdown directories",
|
|
3933
|
+
package: "mcp-obsidian",
|
|
3451
3934
|
category: "project-mgmt",
|
|
3452
3935
|
requiresConfig: true,
|
|
3453
3936
|
configFields: [
|
|
3454
3937
|
{
|
|
3455
|
-
name: "
|
|
3456
|
-
type: "string",
|
|
3457
|
-
required: false,
|
|
3458
|
-
description: "Asana Personal Access Token",
|
|
3459
|
-
envVar: "ASANA_TOKEN"
|
|
3460
|
-
}
|
|
3461
|
-
],
|
|
3462
|
-
installInstructions: "Create a PAT at https://app.asana.com/0/my-apps"
|
|
3463
|
-
},
|
|
3464
|
-
// ============================================
|
|
3465
|
-
// MONITORING & OBSERVABILITY
|
|
3466
|
-
// ============================================
|
|
3467
|
-
{
|
|
3468
|
-
id: "sentry",
|
|
3469
|
-
name: "Sentry",
|
|
3470
|
-
description: "Error monitoring and tracking",
|
|
3471
|
-
package: "@sentry/mcp-server",
|
|
3472
|
-
category: "monitoring",
|
|
3473
|
-
requiresConfig: true,
|
|
3474
|
-
configFields: [
|
|
3475
|
-
{
|
|
3476
|
-
name: "authToken",
|
|
3477
|
-
type: "string",
|
|
3478
|
-
required: false,
|
|
3479
|
-
description: "Sentry Auth Token",
|
|
3480
|
-
envVar: "SENTRY_AUTH_TOKEN"
|
|
3481
|
-
},
|
|
3482
|
-
{
|
|
3483
|
-
name: "org",
|
|
3484
|
-
type: "string",
|
|
3485
|
-
required: false,
|
|
3486
|
-
description: "Sentry Organization slug"
|
|
3487
|
-
}
|
|
3488
|
-
],
|
|
3489
|
-
installInstructions: "Create an auth token at https://sentry.io/settings/account/api/auth-tokens/"
|
|
3490
|
-
},
|
|
3491
|
-
{
|
|
3492
|
-
id: "datadog",
|
|
3493
|
-
name: "Datadog",
|
|
3494
|
-
description: "Datadog monitoring and APM",
|
|
3495
|
-
package: "@anthropic/datadog-mcp",
|
|
3496
|
-
category: "monitoring",
|
|
3497
|
-
requiresConfig: true,
|
|
3498
|
-
configFields: [
|
|
3499
|
-
{
|
|
3500
|
-
name: "apiKey",
|
|
3938
|
+
name: "vaultPath",
|
|
3501
3939
|
type: "string",
|
|
3502
|
-
required:
|
|
3503
|
-
description: "
|
|
3504
|
-
envVar: "DD_API_KEY"
|
|
3505
|
-
},
|
|
3506
|
-
{
|
|
3507
|
-
name: "appKey",
|
|
3508
|
-
type: "string",
|
|
3509
|
-
required: false,
|
|
3510
|
-
description: "Datadog Application Key",
|
|
3511
|
-
envVar: "DD_APP_KEY"
|
|
3512
|
-
},
|
|
3513
|
-
{
|
|
3514
|
-
name: "site",
|
|
3515
|
-
type: "string",
|
|
3516
|
-
required: false,
|
|
3517
|
-
description: "Datadog site (e.g., datadoghq.com)",
|
|
3518
|
-
default: "datadoghq.com"
|
|
3940
|
+
required: true,
|
|
3941
|
+
description: "Path to Obsidian vault"
|
|
3519
3942
|
}
|
|
3520
3943
|
],
|
|
3521
|
-
installInstructions: "
|
|
3944
|
+
installInstructions: "Works with any Markdown directory. Point to your vault path."
|
|
3522
3945
|
},
|
|
3523
|
-
// ============================================
|
|
3524
|
-
// COMMUNICATION
|
|
3525
|
-
// ============================================
|
|
3526
3946
|
{
|
|
3527
|
-
id: "
|
|
3528
|
-
name: "
|
|
3529
|
-
description: "
|
|
3530
|
-
package: "
|
|
3531
|
-
category: "
|
|
3532
|
-
requiresConfig:
|
|
3533
|
-
|
|
3534
|
-
{
|
|
3535
|
-
name: "token",
|
|
3536
|
-
type: "string",
|
|
3537
|
-
required: false,
|
|
3538
|
-
description: "Slack Bot Token (xoxb-...)",
|
|
3539
|
-
envVar: "SLACK_BOT_TOKEN"
|
|
3540
|
-
}
|
|
3541
|
-
],
|
|
3542
|
-
installInstructions: "Create a Slack app at https://api.slack.com/apps and install to your workspace."
|
|
3543
|
-
},
|
|
3544
|
-
{
|
|
3545
|
-
id: "discord",
|
|
3546
|
-
name: "Discord",
|
|
3547
|
-
description: "Discord bot and server management",
|
|
3548
|
-
package: "@anthropic/discord-mcp",
|
|
3549
|
-
category: "communication",
|
|
3550
|
-
requiresConfig: true,
|
|
3551
|
-
configFields: [
|
|
3552
|
-
{
|
|
3553
|
-
name: "token",
|
|
3554
|
-
type: "string",
|
|
3555
|
-
required: false,
|
|
3556
|
-
description: "Discord Bot Token",
|
|
3557
|
-
envVar: "DISCORD_BOT_TOKEN"
|
|
3558
|
-
}
|
|
3559
|
-
],
|
|
3560
|
-
installInstructions: "Create a bot at https://discord.com/developers/applications"
|
|
3947
|
+
id: "n8n",
|
|
3948
|
+
name: "n8n",
|
|
3949
|
+
description: "n8n workflow automation node documentation and management",
|
|
3950
|
+
package: "n8n-mcp",
|
|
3951
|
+
category: "project-mgmt",
|
|
3952
|
+
requiresConfig: false,
|
|
3953
|
+
installInstructions: "Provides access to 543 n8n nodes documentation."
|
|
3561
3954
|
},
|
|
3562
3955
|
// ============================================
|
|
3563
|
-
//
|
|
3956
|
+
// MONITORING & OBSERVABILITY
|
|
3564
3957
|
// ============================================
|
|
3565
3958
|
{
|
|
3566
|
-
id: "
|
|
3567
|
-
name: "
|
|
3568
|
-
description: "
|
|
3569
|
-
package: "@
|
|
3570
|
-
category: "
|
|
3959
|
+
id: "sentry",
|
|
3960
|
+
name: "Sentry",
|
|
3961
|
+
description: "Query Sentry errors, issues, and project information",
|
|
3962
|
+
package: "@sentry/mcp-server",
|
|
3963
|
+
category: "monitoring",
|
|
3571
3964
|
requiresConfig: true,
|
|
3572
3965
|
configFields: [
|
|
3573
3966
|
{
|
|
3574
|
-
name: "
|
|
3967
|
+
name: "authToken",
|
|
3575
3968
|
type: "string",
|
|
3576
3969
|
required: false,
|
|
3577
|
-
description: "
|
|
3578
|
-
envVar: "
|
|
3970
|
+
description: "Sentry Auth Token",
|
|
3971
|
+
envVar: "SENTRY_AUTH_TOKEN"
|
|
3579
3972
|
}
|
|
3580
3973
|
],
|
|
3581
|
-
installInstructions: "
|
|
3974
|
+
installInstructions: "For Claude Code: claude mcp add --transport http sentry https://mcp.sentry.dev/mcp"
|
|
3582
3975
|
},
|
|
3583
3976
|
// ============================================
|
|
3584
|
-
//
|
|
3977
|
+
// COMMUNICATION
|
|
3585
3978
|
// ============================================
|
|
3586
3979
|
{
|
|
3587
|
-
id: "
|
|
3588
|
-
name: "
|
|
3589
|
-
description: "
|
|
3590
|
-
package: "@
|
|
3591
|
-
category: "
|
|
3980
|
+
id: "slack",
|
|
3981
|
+
name: "Slack",
|
|
3982
|
+
description: "Slack messaging, channels, and workspace interaction",
|
|
3983
|
+
package: "@modelcontextprotocol/server-slack",
|
|
3984
|
+
category: "communication",
|
|
3592
3985
|
requiresConfig: true,
|
|
3593
3986
|
configFields: [
|
|
3594
3987
|
{
|
|
3595
|
-
name: "
|
|
3988
|
+
name: "token",
|
|
3596
3989
|
type: "string",
|
|
3597
3990
|
required: false,
|
|
3598
|
-
description: "
|
|
3599
|
-
envVar: "
|
|
3991
|
+
description: "Slack Bot Token (xoxb-...)",
|
|
3992
|
+
envVar: "SLACK_BOT_TOKEN"
|
|
3600
3993
|
}
|
|
3601
3994
|
],
|
|
3602
|
-
installInstructions: "
|
|
3995
|
+
installInstructions: "Create a Slack app at https://api.slack.com/apps and install to your workspace."
|
|
3603
3996
|
},
|
|
3604
3997
|
// ============================================
|
|
3605
|
-
//
|
|
3998
|
+
// DESIGN
|
|
3606
3999
|
// ============================================
|
|
3607
4000
|
{
|
|
3608
|
-
id: "
|
|
3609
|
-
name: "
|
|
3610
|
-
description: "
|
|
3611
|
-
package: "
|
|
3612
|
-
category: "
|
|
4001
|
+
id: "figma",
|
|
4002
|
+
name: "Figma",
|
|
4003
|
+
description: "Figma layout information for AI coding agents",
|
|
4004
|
+
package: "figma-developer-mcp",
|
|
4005
|
+
category: "design",
|
|
3613
4006
|
requiresConfig: true,
|
|
3614
4007
|
configFields: [
|
|
3615
|
-
{
|
|
3616
|
-
name: "appId",
|
|
3617
|
-
type: "string",
|
|
3618
|
-
required: false,
|
|
3619
|
-
description: "Algolia Application ID",
|
|
3620
|
-
envVar: "ALGOLIA_APP_ID"
|
|
3621
|
-
},
|
|
3622
4008
|
{
|
|
3623
4009
|
name: "apiKey",
|
|
3624
4010
|
type: "string",
|
|
3625
|
-
required:
|
|
3626
|
-
description: "
|
|
3627
|
-
envVar: "
|
|
4011
|
+
required: true,
|
|
4012
|
+
description: "Figma Personal Access Token",
|
|
4013
|
+
envVar: "FIGMA_API_KEY"
|
|
3628
4014
|
}
|
|
3629
4015
|
],
|
|
3630
|
-
installInstructions: "
|
|
4016
|
+
installInstructions: "Create token at https://www.figma.com/developers/api#access-tokens"
|
|
3631
4017
|
},
|
|
3632
4018
|
{
|
|
3633
|
-
id: "
|
|
3634
|
-
name: "
|
|
3635
|
-
description: "
|
|
3636
|
-
package: "@
|
|
3637
|
-
category: "
|
|
4019
|
+
id: "shadcn",
|
|
4020
|
+
name: "shadcn/ui",
|
|
4021
|
+
description: "shadcn/ui component docs, installation, and code generation",
|
|
4022
|
+
package: "@heilgar/shadcn-ui-mcp-server",
|
|
4023
|
+
category: "ui-library",
|
|
4024
|
+
requiresConfig: false,
|
|
4025
|
+
installInstructions: "Supports npm, pnpm, yarn, and bun package managers."
|
|
4026
|
+
},
|
|
4027
|
+
{
|
|
4028
|
+
id: "magic-ui",
|
|
4029
|
+
name: "21st.dev Magic",
|
|
4030
|
+
description: "AI-driven UI component generation through natural language",
|
|
4031
|
+
package: "@21st-dev/magic",
|
|
4032
|
+
category: "ui-library",
|
|
3638
4033
|
requiresConfig: true,
|
|
3639
4034
|
configFields: [
|
|
3640
|
-
{
|
|
3641
|
-
name: "node",
|
|
3642
|
-
type: "string",
|
|
3643
|
-
required: false,
|
|
3644
|
-
description: "Elasticsearch node URL",
|
|
3645
|
-
envVar: "ELASTICSEARCH_NODE",
|
|
3646
|
-
default: "http://localhost:9200"
|
|
3647
|
-
},
|
|
3648
4035
|
{
|
|
3649
4036
|
name: "apiKey",
|
|
3650
4037
|
type: "string",
|
|
3651
|
-
required:
|
|
3652
|
-
description: "
|
|
3653
|
-
envVar: "
|
|
4038
|
+
required: true,
|
|
4039
|
+
description: "21st.dev Magic API Key",
|
|
4040
|
+
envVar: "TWENTYFIRST_API_KEY"
|
|
3654
4041
|
}
|
|
3655
|
-
]
|
|
4042
|
+
],
|
|
4043
|
+
installInstructions: "Get API key at https://21st.dev/magic/console"
|
|
3656
4044
|
},
|
|
3657
4045
|
// ============================================
|
|
3658
|
-
//
|
|
4046
|
+
// PAYMENTS
|
|
3659
4047
|
// ============================================
|
|
3660
4048
|
{
|
|
3661
|
-
id: "
|
|
3662
|
-
name: "
|
|
3663
|
-
description: "
|
|
3664
|
-
package: "@
|
|
3665
|
-
category: "
|
|
4049
|
+
id: "stripe",
|
|
4050
|
+
name: "Stripe",
|
|
4051
|
+
description: "Stripe payments API with MCP support via Agent Toolkit",
|
|
4052
|
+
package: "@stripe/agent-toolkit",
|
|
4053
|
+
category: "payments",
|
|
3666
4054
|
requiresConfig: true,
|
|
3667
4055
|
configFields: [
|
|
3668
4056
|
{
|
|
3669
|
-
name: "
|
|
4057
|
+
name: "secretKey",
|
|
3670
4058
|
type: "string",
|
|
3671
4059
|
required: false,
|
|
3672
|
-
description: "
|
|
3673
|
-
envVar: "
|
|
4060
|
+
description: "Stripe Secret Key",
|
|
4061
|
+
envVar: "STRIPE_SECRET_KEY"
|
|
3674
4062
|
}
|
|
3675
4063
|
],
|
|
3676
|
-
installInstructions: "Get
|
|
4064
|
+
installInstructions: "Get API keys at https://dashboard.stripe.com/apikeys. Run: npx -y @stripe/mcp --tools=all --api-key=YOUR_KEY"
|
|
3677
4065
|
},
|
|
3678
4066
|
{
|
|
3679
|
-
id: "
|
|
3680
|
-
name: "
|
|
3681
|
-
description: "
|
|
3682
|
-
package: "
|
|
3683
|
-
category: "
|
|
4067
|
+
id: "mercadopago",
|
|
4068
|
+
name: "Mercado Pago",
|
|
4069
|
+
description: "Mercado Pago payments, refunds, and customer management",
|
|
4070
|
+
package: "mercado-pago-mcp",
|
|
4071
|
+
category: "payments",
|
|
3684
4072
|
requiresConfig: true,
|
|
3685
4073
|
configFields: [
|
|
3686
4074
|
{
|
|
3687
|
-
name: "
|
|
4075
|
+
name: "accessToken",
|
|
3688
4076
|
type: "string",
|
|
3689
|
-
required:
|
|
3690
|
-
description: "
|
|
3691
|
-
envVar: "
|
|
4077
|
+
required: true,
|
|
4078
|
+
description: "Mercado Pago Access Token",
|
|
4079
|
+
envVar: "MERCADOPAGO_ACCESS_TOKEN"
|
|
3692
4080
|
},
|
|
3693
4081
|
{
|
|
3694
4082
|
name: "environment",
|
|
3695
4083
|
type: "string",
|
|
3696
4084
|
required: false,
|
|
3697
|
-
description: "
|
|
3698
|
-
|
|
4085
|
+
description: "Environment (sandbox or production)",
|
|
4086
|
+
default: "sandbox"
|
|
3699
4087
|
}
|
|
3700
4088
|
],
|
|
3701
|
-
installInstructions: "Get credentials at https://
|
|
4089
|
+
installInstructions: "Get credentials at https://www.mercadopago.com/developers/panel/app"
|
|
3702
4090
|
},
|
|
3703
4091
|
// ============================================
|
|
3704
|
-
//
|
|
4092
|
+
// SEARCH
|
|
3705
4093
|
// ============================================
|
|
3706
4094
|
{
|
|
3707
|
-
id: "
|
|
3708
|
-
name: "
|
|
3709
|
-
description: "
|
|
3710
|
-
package: "@
|
|
3711
|
-
category: "
|
|
4095
|
+
id: "brave-search",
|
|
4096
|
+
name: "Brave Search",
|
|
4097
|
+
description: "Web and local search using Brave Search API",
|
|
4098
|
+
package: "@modelcontextprotocol/server-brave-search",
|
|
4099
|
+
category: "search",
|
|
3712
4100
|
requiresConfig: true,
|
|
3713
4101
|
configFields: [
|
|
3714
4102
|
{
|
|
3715
|
-
name: "
|
|
3716
|
-
type: "string",
|
|
3717
|
-
required: false,
|
|
3718
|
-
description: "Vault server address",
|
|
3719
|
-
envVar: "VAULT_ADDR",
|
|
3720
|
-
default: "http://127.0.0.1:8200"
|
|
3721
|
-
},
|
|
3722
|
-
{
|
|
3723
|
-
name: "token",
|
|
4103
|
+
name: "apiKey",
|
|
3724
4104
|
type: "string",
|
|
3725
4105
|
required: false,
|
|
3726
|
-
description: "
|
|
3727
|
-
envVar: "
|
|
4106
|
+
description: "Brave Search API Key",
|
|
4107
|
+
envVar: "BRAVE_API_KEY"
|
|
3728
4108
|
}
|
|
3729
|
-
]
|
|
4109
|
+
],
|
|
4110
|
+
installInstructions: "Get an API key at https://brave.com/search/api/"
|
|
3730
4111
|
}
|
|
3731
4112
|
];
|
|
3732
4113
|
function getMcpServer(id) {
|
|
@@ -4064,6 +4445,29 @@ async function installModules(category, modules, options) {
|
|
|
4064
4445
|
}
|
|
4065
4446
|
return result;
|
|
4066
4447
|
}
|
|
4448
|
+
async function installCategoryReadme(category, options) {
|
|
4449
|
+
const sourceReadme = joinPath(options.templatesPath, category, "README.md");
|
|
4450
|
+
const targetReadme = joinPath(options.targetPath, ".claude", category, "README.md");
|
|
4451
|
+
if (!await pathExists(sourceReadme)) {
|
|
4452
|
+
logger.debug(`No README found for category: ${category}`);
|
|
4453
|
+
return;
|
|
4454
|
+
}
|
|
4455
|
+
const targetExists = await pathExists(targetReadme);
|
|
4456
|
+
if (targetExists && !options.overwrite) {
|
|
4457
|
+
logger.debug(`README already exists for ${category}, skipping`);
|
|
4458
|
+
return;
|
|
4459
|
+
}
|
|
4460
|
+
if (options.dryRun) {
|
|
4461
|
+
logger.debug(`Would install README for ${category}`);
|
|
4462
|
+
return;
|
|
4463
|
+
}
|
|
4464
|
+
try {
|
|
4465
|
+
await copy(sourceReadme, targetReadme, { overwrite: options.overwrite });
|
|
4466
|
+
logger.debug(`Installed README for ${category}`);
|
|
4467
|
+
} catch (error) {
|
|
4468
|
+
logger.debug(`Failed to install README for ${category}: ${error}`);
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4067
4471
|
async function installAllModules(modulesByCategory, options) {
|
|
4068
4472
|
const results = {};
|
|
4069
4473
|
const categories = ["agents", "skills", "commands", "docs"];
|
|
@@ -4086,6 +4490,7 @@ async function installAllModules(modulesByCategory, options) {
|
|
|
4086
4490
|
silent: options.dryRun
|
|
4087
4491
|
}
|
|
4088
4492
|
);
|
|
4493
|
+
await installCategoryReadme(category, options);
|
|
4089
4494
|
}
|
|
4090
4495
|
return results;
|
|
4091
4496
|
}
|
|
@@ -4596,7 +5001,7 @@ async function updatePackageJson(projectPath, changes, options = {}) {
|
|
|
4596
5001
|
return result;
|
|
4597
5002
|
}
|
|
4598
5003
|
}
|
|
4599
|
-
function
|
|
5004
|
+
function getInstallCommand2(packageManager) {
|
|
4600
5005
|
switch (packageManager) {
|
|
4601
5006
|
case "npm":
|
|
4602
5007
|
return "npm install";
|
|
@@ -5301,6 +5706,218 @@ function showReplacementReport(report) {
|
|
|
5301
5706
|
}
|
|
5302
5707
|
}
|
|
5303
5708
|
|
|
5709
|
+
// src/lib/scaffold/claude-md-generator.ts
|
|
5710
|
+
init_esm_shims();
|
|
5711
|
+
init_fs();
|
|
5712
|
+
|
|
5713
|
+
// src/lib/utils/paths.ts
|
|
5714
|
+
init_esm_shims();
|
|
5715
|
+
import fs3 from "fs";
|
|
5716
|
+
import path5 from "path";
|
|
5717
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5718
|
+
function getPackageRoot() {
|
|
5719
|
+
const currentFilePath = fileURLToPath2(import.meta.url);
|
|
5720
|
+
let currentDir = path5.dirname(currentFilePath);
|
|
5721
|
+
while (currentDir !== path5.dirname(currentDir)) {
|
|
5722
|
+
const packageJsonPath = path5.join(currentDir, "package.json");
|
|
5723
|
+
if (fs3.existsSync(packageJsonPath)) {
|
|
5724
|
+
return currentDir;
|
|
5725
|
+
}
|
|
5726
|
+
currentDir = path5.dirname(currentDir);
|
|
5727
|
+
}
|
|
5728
|
+
throw new Error("Could not find package root (no package.json found in parent directories)");
|
|
5729
|
+
}
|
|
5730
|
+
function getTemplatesPath() {
|
|
5731
|
+
return path5.join(getPackageRoot(), "templates");
|
|
5732
|
+
}
|
|
5733
|
+
|
|
5734
|
+
// src/lib/scaffold/claude-md-generator.ts
|
|
5735
|
+
async function generateClaudeMd(projectPath, projectInfo, options) {
|
|
5736
|
+
const claudeMdPath = joinPath(projectPath, "CLAUDE.md");
|
|
5737
|
+
const exists = await pathExists(claudeMdPath);
|
|
5738
|
+
if (exists && !options?.overwrite) {
|
|
5739
|
+
return {
|
|
5740
|
+
created: false,
|
|
5741
|
+
skipped: true,
|
|
5742
|
+
path: claudeMdPath
|
|
5743
|
+
};
|
|
5744
|
+
}
|
|
5745
|
+
try {
|
|
5746
|
+
let template;
|
|
5747
|
+
if (options?.customTemplate) {
|
|
5748
|
+
template = options.customTemplate;
|
|
5749
|
+
} else {
|
|
5750
|
+
const templatePath = joinPath(getTemplatesPath(), "CLAUDE.md.template");
|
|
5751
|
+
if (await pathExists(templatePath)) {
|
|
5752
|
+
template = await readFile(templatePath);
|
|
5753
|
+
} else {
|
|
5754
|
+
template = getMinimalTemplate();
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5757
|
+
const content = processTemplate(template, projectInfo, options);
|
|
5758
|
+
await writeFile(claudeMdPath, content);
|
|
5759
|
+
return {
|
|
5760
|
+
created: true,
|
|
5761
|
+
skipped: false,
|
|
5762
|
+
path: claudeMdPath
|
|
5763
|
+
};
|
|
5764
|
+
} catch (error) {
|
|
5765
|
+
return {
|
|
5766
|
+
created: false,
|
|
5767
|
+
skipped: false,
|
|
5768
|
+
path: claudeMdPath,
|
|
5769
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5770
|
+
};
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
async function generateClaudeMdWithSpinner(projectPath, projectInfo, options) {
|
|
5774
|
+
return withSpinner(
|
|
5775
|
+
"Generating CLAUDE.md...",
|
|
5776
|
+
() => generateClaudeMd(projectPath, projectInfo, options),
|
|
5777
|
+
{
|
|
5778
|
+
successText: "Created CLAUDE.md"
|
|
5779
|
+
}
|
|
5780
|
+
);
|
|
5781
|
+
}
|
|
5782
|
+
function processTemplate(template, projectInfo, options) {
|
|
5783
|
+
let content = template;
|
|
5784
|
+
const techStack = options?.templateConfig?.techStack;
|
|
5785
|
+
const commands = options?.templateConfig?.commands;
|
|
5786
|
+
const targets = options?.templateConfig?.targets;
|
|
5787
|
+
const preferences = options?.claudeConfig?.preferences;
|
|
5788
|
+
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 || "");
|
|
5789
|
+
const packageManager = preferences?.packageManager || "pnpm";
|
|
5790
|
+
content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
|
|
5791
|
+
const coverageTarget = targets && "coverage" in targets ? String(targets.coverage) : "90";
|
|
5792
|
+
content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
|
|
5793
|
+
if (projectInfo.domain) {
|
|
5794
|
+
content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
|
|
5795
|
+
} else {
|
|
5796
|
+
content = content.replace(/\{\{#if DOMAIN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
5797
|
+
}
|
|
5798
|
+
if (techStack && Object.keys(techStack).length > 0) {
|
|
5799
|
+
const techStackContent = generateTechStackSection(techStack);
|
|
5800
|
+
content = content.replace(/\{\{#if TECH_STACK\}\}/g, "").replace(/\{\{TECH_STACK\}\}\n\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, techStackContent);
|
|
5801
|
+
} else {
|
|
5802
|
+
content = content.replace(/\{\{#if TECH_STACK\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
5803
|
+
}
|
|
5804
|
+
if (commands && Object.keys(commands).length > 0) {
|
|
5805
|
+
const commandsContent = generateCommandsSection(commands, packageManager);
|
|
5806
|
+
content = content.replace(/\{\{#if COMMANDS\}\}/g, "").replace(/\{\{COMMANDS\}\}\n\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, commandsContent);
|
|
5807
|
+
} else {
|
|
5808
|
+
content = content.replace(/\{\{#if COMMANDS\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
5809
|
+
}
|
|
5810
|
+
content = content.replace(/\{\{#if PROJECT_STRUCTURE\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
5811
|
+
return content;
|
|
5812
|
+
}
|
|
5813
|
+
function generateTechStackSection(techStack) {
|
|
5814
|
+
const lines = [];
|
|
5815
|
+
if (techStack.frontendFramework && techStack.frontendFramework !== "None") {
|
|
5816
|
+
lines.push("**Frontend:**");
|
|
5817
|
+
lines.push(`- Framework: ${techStack.frontendFramework}`);
|
|
5818
|
+
if (techStack.stateManagement && techStack.stateManagement !== "None") {
|
|
5819
|
+
lines.push(`- State: ${techStack.stateManagement}`);
|
|
5820
|
+
}
|
|
5821
|
+
lines.push("");
|
|
5822
|
+
}
|
|
5823
|
+
if (techStack.apiFramework && techStack.apiFramework !== "None") {
|
|
5824
|
+
lines.push("**Backend:**");
|
|
5825
|
+
lines.push(`- API: ${techStack.apiFramework}`);
|
|
5826
|
+
if (techStack.validationLibrary && techStack.validationLibrary !== "None") {
|
|
5827
|
+
lines.push(`- Validation: ${techStack.validationLibrary}`);
|
|
5828
|
+
}
|
|
5829
|
+
lines.push("");
|
|
5830
|
+
}
|
|
5831
|
+
if (techStack.databaseOrm && techStack.databaseOrm !== "None") {
|
|
5832
|
+
lines.push("**Database:**");
|
|
5833
|
+
lines.push(`- ORM: ${techStack.databaseOrm}`);
|
|
5834
|
+
lines.push("");
|
|
5835
|
+
}
|
|
5836
|
+
if (techStack.authPattern && techStack.authPattern !== "None") {
|
|
5837
|
+
lines.push("**Authentication:**");
|
|
5838
|
+
lines.push(`- Provider: ${techStack.authPattern}`);
|
|
5839
|
+
lines.push("");
|
|
5840
|
+
}
|
|
5841
|
+
if (techStack.testFramework && techStack.testFramework !== "None") {
|
|
5842
|
+
lines.push("**Testing:**");
|
|
5843
|
+
lines.push(`- Framework: ${techStack.testFramework}`);
|
|
5844
|
+
lines.push("");
|
|
5845
|
+
}
|
|
5846
|
+
if (techStack.bundler && techStack.bundler !== "None") {
|
|
5847
|
+
lines.push("**Build:**");
|
|
5848
|
+
lines.push(`- Bundler: ${techStack.bundler}`);
|
|
5849
|
+
lines.push("");
|
|
5850
|
+
}
|
|
5851
|
+
return lines.join("\n");
|
|
5852
|
+
}
|
|
5853
|
+
function generateCommandsSection(commands, packageManager) {
|
|
5854
|
+
const lines = ["```bash"];
|
|
5855
|
+
lines.push("# Development");
|
|
5856
|
+
lines.push(`${packageManager} dev # Start development server`);
|
|
5857
|
+
lines.push("");
|
|
5858
|
+
lines.push("# Testing");
|
|
5859
|
+
if (commands.test) {
|
|
5860
|
+
lines.push(`${commands.test} # Run tests`);
|
|
5861
|
+
} else {
|
|
5862
|
+
lines.push(`${packageManager} test # Run tests`);
|
|
5863
|
+
}
|
|
5864
|
+
if (commands.coverage) {
|
|
5865
|
+
lines.push(`${commands.coverage} # Run tests with coverage`);
|
|
5866
|
+
} else {
|
|
5867
|
+
lines.push(`${packageManager} test:coverage # Run tests with coverage`);
|
|
5868
|
+
}
|
|
5869
|
+
lines.push("");
|
|
5870
|
+
lines.push("# Quality");
|
|
5871
|
+
if (commands.lint) {
|
|
5872
|
+
lines.push(`${commands.lint} # Run linter`);
|
|
5873
|
+
} else {
|
|
5874
|
+
lines.push(`${packageManager} lint # Run linter`);
|
|
5875
|
+
}
|
|
5876
|
+
if (commands.typecheck) {
|
|
5877
|
+
lines.push(`${commands.typecheck} # Type checking`);
|
|
5878
|
+
} else {
|
|
5879
|
+
lines.push(`${packageManager} typecheck # Type checking`);
|
|
5880
|
+
}
|
|
5881
|
+
if (commands.build) {
|
|
5882
|
+
lines.push("");
|
|
5883
|
+
lines.push("# Build");
|
|
5884
|
+
lines.push(`${commands.build} # Build for production`);
|
|
5885
|
+
}
|
|
5886
|
+
lines.push("```");
|
|
5887
|
+
return lines.join("\n");
|
|
5888
|
+
}
|
|
5889
|
+
function getMinimalTemplate() {
|
|
5890
|
+
return `# CLAUDE.md
|
|
5891
|
+
|
|
5892
|
+
## Project Overview
|
|
5893
|
+
|
|
5894
|
+
**{{PROJECT_NAME}}** - {{PROJECT_DESCRIPTION}}
|
|
5895
|
+
|
|
5896
|
+
## Repository
|
|
5897
|
+
|
|
5898
|
+
- **GitHub:** https://github.com/{{ORG}}/{{REPO}}
|
|
5899
|
+
|
|
5900
|
+
## Quick Commands
|
|
5901
|
+
|
|
5902
|
+
\`\`\`bash
|
|
5903
|
+
{{PACKAGE_MANAGER}} dev # Start development
|
|
5904
|
+
{{PACKAGE_MANAGER}} test # Run tests
|
|
5905
|
+
{{PACKAGE_MANAGER}} lint # Run linter
|
|
5906
|
+
{{PACKAGE_MANAGER}} build # Build project
|
|
5907
|
+
\`\`\`
|
|
5908
|
+
|
|
5909
|
+
## Claude Configuration
|
|
5910
|
+
|
|
5911
|
+
This project uses \`@qazuor/claude-code-config\` for AI-assisted development.
|
|
5912
|
+
|
|
5913
|
+
See \`.claude/docs/quick-start.md\` for getting started.
|
|
5914
|
+
|
|
5915
|
+
---
|
|
5916
|
+
|
|
5917
|
+
*Generated by [@qazuor/claude-code-config](https://github.com/qazuor/claude-code-config)*
|
|
5918
|
+
`;
|
|
5919
|
+
}
|
|
5920
|
+
|
|
5304
5921
|
// src/lib/scaffold/index.ts
|
|
5305
5922
|
init_esm_shims();
|
|
5306
5923
|
|
|
@@ -5816,8 +6433,8 @@ async function generateScaffoldWithProgress(projectPath, options) {
|
|
|
5816
6433
|
|
|
5817
6434
|
// src/lib/templates/config-replacer.ts
|
|
5818
6435
|
init_esm_shims();
|
|
5819
|
-
import * as
|
|
5820
|
-
import * as
|
|
6436
|
+
import * as fs4 from "fs/promises";
|
|
6437
|
+
import * as path6 from "path";
|
|
5821
6438
|
import ora2 from "ora";
|
|
5822
6439
|
|
|
5823
6440
|
// src/constants/template-placeholders.ts
|
|
@@ -6231,16 +6848,19 @@ var TEMPLATE_PLACEHOLDERS = [
|
|
|
6231
6848
|
choices: [
|
|
6232
6849
|
{ name: "React", value: "React" },
|
|
6233
6850
|
{ name: "Next.js", value: "Next.js" },
|
|
6851
|
+
{ name: "TanStack Start", value: "TanStack Start" },
|
|
6234
6852
|
{ name: "Vue", value: "Vue" },
|
|
6235
6853
|
{ name: "Nuxt", value: "Nuxt" },
|
|
6236
6854
|
{ name: "Svelte", value: "Svelte" },
|
|
6237
6855
|
{ name: "SvelteKit", value: "SvelteKit" },
|
|
6238
6856
|
{ name: "Astro", value: "Astro" },
|
|
6239
6857
|
{ name: "SolidJS", value: "SolidJS" },
|
|
6858
|
+
{ name: "Remix", value: "Remix" },
|
|
6240
6859
|
{ name: "Angular", value: "Angular" },
|
|
6241
6860
|
{ name: "None", value: "None" }
|
|
6242
6861
|
],
|
|
6243
6862
|
default: (ctx) => {
|
|
6863
|
+
if (hasDependency(ctx, "@tanstack/start")) return "TanStack Start";
|
|
6244
6864
|
if (hasDependency(ctx, "next")) return "Next.js";
|
|
6245
6865
|
if (hasDependency(ctx, "nuxt")) return "Nuxt";
|
|
6246
6866
|
if (hasDependency(ctx, "vue")) return "Vue";
|
|
@@ -6248,6 +6868,7 @@ var TEMPLATE_PLACEHOLDERS = [
|
|
|
6248
6868
|
if (hasDependency(ctx, "@sveltejs/kit")) return "SvelteKit";
|
|
6249
6869
|
if (hasDependency(ctx, "astro")) return "Astro";
|
|
6250
6870
|
if (hasDependency(ctx, "solid-js")) return "SolidJS";
|
|
6871
|
+
if (hasDependency(ctx, "@remix-run/react")) return "Remix";
|
|
6251
6872
|
if (hasDependency(ctx, "@angular/core")) return "Angular";
|
|
6252
6873
|
if (hasDependency(ctx, "react")) return "React";
|
|
6253
6874
|
return "None";
|
|
@@ -6319,26 +6940,32 @@ var TEMPLATE_PLACEHOLDERS = [
|
|
|
6319
6940
|
description: "Authentication approach",
|
|
6320
6941
|
inputType: "select",
|
|
6321
6942
|
choices: [
|
|
6943
|
+
{ name: "Better Auth", value: "Better Auth" },
|
|
6322
6944
|
{ name: "Clerk", value: "Clerk" },
|
|
6323
6945
|
{ name: "Auth.js (NextAuth)", value: "Auth.js" },
|
|
6324
6946
|
{ name: "Lucia", value: "Lucia" },
|
|
6325
6947
|
{ name: "Firebase Auth", value: "Firebase" },
|
|
6326
6948
|
{ name: "Supabase Auth", value: "Supabase" },
|
|
6949
|
+
{ name: "Kinde", value: "Kinde" },
|
|
6950
|
+
{ name: "WorkOS", value: "WorkOS" },
|
|
6327
6951
|
{ name: "Custom JWT", value: "JWT" },
|
|
6328
6952
|
{ name: "Session-based", value: "Session" },
|
|
6329
6953
|
{ name: "None", value: "None" }
|
|
6330
6954
|
],
|
|
6331
6955
|
default: (ctx) => {
|
|
6956
|
+
if (hasDependency(ctx, "better-auth")) return "Better Auth";
|
|
6332
6957
|
if (hasDependency(ctx, "@clerk/nextjs") || hasDependency(ctx, "@clerk/clerk-react"))
|
|
6333
6958
|
return "Clerk";
|
|
6334
6959
|
if (hasDependency(ctx, "next-auth") || hasDependency(ctx, "@auth/core")) return "Auth.js";
|
|
6335
6960
|
if (hasDependency(ctx, "lucia")) return "Lucia";
|
|
6336
6961
|
if (hasDependency(ctx, "firebase")) return "Firebase";
|
|
6337
6962
|
if (hasDependency(ctx, "@supabase/supabase-js")) return "Supabase";
|
|
6963
|
+
if (hasDependency(ctx, "@kinde-oss/kinde-auth-nextjs")) return "Kinde";
|
|
6964
|
+
if (hasDependency(ctx, "@workos-inc/authkit-nextjs")) return "WorkOS";
|
|
6338
6965
|
return "None";
|
|
6339
6966
|
},
|
|
6340
6967
|
required: false,
|
|
6341
|
-
example: "
|
|
6968
|
+
example: "Better Auth"
|
|
6342
6969
|
},
|
|
6343
6970
|
{
|
|
6344
6971
|
key: "STATE_MANAGEMENT",
|
|
@@ -6715,7 +7342,7 @@ function flattenTemplateConfig(config) {
|
|
|
6715
7342
|
return flattened;
|
|
6716
7343
|
}
|
|
6717
7344
|
function shouldProcessFile(filePath) {
|
|
6718
|
-
const ext =
|
|
7345
|
+
const ext = path6.extname(filePath).toLowerCase();
|
|
6719
7346
|
return PROCESSABLE_EXTENSIONS.includes(ext);
|
|
6720
7347
|
}
|
|
6721
7348
|
function shouldSkipDirectory(dirName) {
|
|
@@ -6724,9 +7351,9 @@ function shouldSkipDirectory(dirName) {
|
|
|
6724
7351
|
async function getAllFiles(dir) {
|
|
6725
7352
|
const files = [];
|
|
6726
7353
|
try {
|
|
6727
|
-
const entries = await
|
|
7354
|
+
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
6728
7355
|
for (const entry of entries) {
|
|
6729
|
-
const fullPath =
|
|
7356
|
+
const fullPath = path6.join(dir, entry.name);
|
|
6730
7357
|
if (entry.isDirectory()) {
|
|
6731
7358
|
if (!shouldSkipDirectory(entry.name)) {
|
|
6732
7359
|
const subFiles = await getAllFiles(fullPath);
|
|
@@ -6743,7 +7370,7 @@ async function getAllFiles(dir) {
|
|
|
6743
7370
|
async function replaceInFile2(filePath, replacements) {
|
|
6744
7371
|
const changes = [];
|
|
6745
7372
|
try {
|
|
6746
|
-
let content = await
|
|
7373
|
+
let content = await fs4.readFile(filePath, "utf-8");
|
|
6747
7374
|
let modified = false;
|
|
6748
7375
|
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
6749
7376
|
if (content.includes(placeholder)) {
|
|
@@ -6753,7 +7380,7 @@ async function replaceInFile2(filePath, replacements) {
|
|
|
6753
7380
|
}
|
|
6754
7381
|
}
|
|
6755
7382
|
if (modified) {
|
|
6756
|
-
await
|
|
7383
|
+
await fs4.writeFile(filePath, content, "utf-8");
|
|
6757
7384
|
}
|
|
6758
7385
|
} catch {
|
|
6759
7386
|
}
|
|
@@ -6774,7 +7401,7 @@ async function replaceTemplatePlaceholders(dir, config) {
|
|
|
6774
7401
|
report.filesModified++;
|
|
6775
7402
|
for (const change of changes) {
|
|
6776
7403
|
report.replacements.push({
|
|
6777
|
-
file:
|
|
7404
|
+
file: path6.relative(dir, file),
|
|
6778
7405
|
placeholder: change.placeholder,
|
|
6779
7406
|
value: change.value
|
|
6780
7407
|
});
|
|
@@ -6845,11 +7472,11 @@ async function previewReplacements(dir, config) {
|
|
|
6845
7472
|
const preview = [];
|
|
6846
7473
|
for (const file of files) {
|
|
6847
7474
|
try {
|
|
6848
|
-
const content = await
|
|
7475
|
+
const content = await fs4.readFile(file, "utf-8");
|
|
6849
7476
|
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
6850
7477
|
if (content.includes(placeholder)) {
|
|
6851
7478
|
preview.push({
|
|
6852
|
-
file:
|
|
7479
|
+
file: path6.relative(dir, file),
|
|
6853
7480
|
placeholder,
|
|
6854
7481
|
value
|
|
6855
7482
|
});
|
|
@@ -6864,27 +7491,6 @@ async function previewReplacements(dir, config) {
|
|
|
6864
7491
|
// src/cli/commands/init.ts
|
|
6865
7492
|
init_fs();
|
|
6866
7493
|
|
|
6867
|
-
// src/lib/utils/paths.ts
|
|
6868
|
-
init_esm_shims();
|
|
6869
|
-
import fs4 from "fs";
|
|
6870
|
-
import path6 from "path";
|
|
6871
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6872
|
-
function getPackageRoot() {
|
|
6873
|
-
const currentFilePath = fileURLToPath2(import.meta.url);
|
|
6874
|
-
let currentDir = path6.dirname(currentFilePath);
|
|
6875
|
-
while (currentDir !== path6.dirname(currentDir)) {
|
|
6876
|
-
const packageJsonPath = path6.join(currentDir, "package.json");
|
|
6877
|
-
if (fs4.existsSync(packageJsonPath)) {
|
|
6878
|
-
return currentDir;
|
|
6879
|
-
}
|
|
6880
|
-
currentDir = path6.dirname(currentDir);
|
|
6881
|
-
}
|
|
6882
|
-
throw new Error("Could not find package root (no package.json found in parent directories)");
|
|
6883
|
-
}
|
|
6884
|
-
function getTemplatesPath() {
|
|
6885
|
-
return path6.join(getPackageRoot(), "templates");
|
|
6886
|
-
}
|
|
6887
|
-
|
|
6888
7494
|
// src/lib/utils/prompt-cancel.ts
|
|
6889
7495
|
init_esm_shims();
|
|
6890
7496
|
import * as readline from "readline";
|
|
@@ -7362,6 +7968,124 @@ async function editBundleSelection(currentBundleIds) {
|
|
|
7362
7968
|
return selected;
|
|
7363
7969
|
}
|
|
7364
7970
|
|
|
7971
|
+
// src/cli/prompts/ci-cd-config.ts
|
|
7972
|
+
init_esm_shims();
|
|
7973
|
+
async function promptCICDConfig(options) {
|
|
7974
|
+
logger.section("CI/CD Configuration", "\u{1F680}");
|
|
7975
|
+
logger.info("Configure continuous integration and deployment workflows");
|
|
7976
|
+
logger.newline();
|
|
7977
|
+
const enableCICD = await confirm({
|
|
7978
|
+
message: "Would you like to set up GitHub Actions workflows?",
|
|
7979
|
+
default: true
|
|
7980
|
+
});
|
|
7981
|
+
if (!enableCICD) {
|
|
7982
|
+
return {
|
|
7983
|
+
enabled: false,
|
|
7984
|
+
provider: "github-actions",
|
|
7985
|
+
ci: false,
|
|
7986
|
+
cd: false,
|
|
7987
|
+
packageManager: options?.packageManager || "pnpm",
|
|
7988
|
+
nodeVersion: "22",
|
|
7989
|
+
enableCaching: true,
|
|
7990
|
+
runTests: true,
|
|
7991
|
+
runLint: true,
|
|
7992
|
+
runTypecheck: true,
|
|
7993
|
+
runBuild: true
|
|
7994
|
+
};
|
|
7995
|
+
}
|
|
7996
|
+
const workflows = await checkbox({
|
|
7997
|
+
message: "Which workflows would you like to create?",
|
|
7998
|
+
choices: [
|
|
7999
|
+
{
|
|
8000
|
+
name: "CI (Continuous Integration) - Lint, test, build on PRs",
|
|
8001
|
+
value: "ci",
|
|
8002
|
+
checked: true
|
|
8003
|
+
},
|
|
8004
|
+
{
|
|
8005
|
+
name: "Release - Create releases on version tags",
|
|
8006
|
+
value: "cd",
|
|
8007
|
+
checked: false
|
|
8008
|
+
}
|
|
8009
|
+
]
|
|
8010
|
+
});
|
|
8011
|
+
if (workflows.length === 0) {
|
|
8012
|
+
return {
|
|
8013
|
+
enabled: false,
|
|
8014
|
+
provider: "github-actions",
|
|
8015
|
+
ci: false,
|
|
8016
|
+
cd: false,
|
|
8017
|
+
packageManager: options?.packageManager || "pnpm",
|
|
8018
|
+
nodeVersion: "22",
|
|
8019
|
+
enableCaching: true,
|
|
8020
|
+
runTests: true,
|
|
8021
|
+
runLint: true,
|
|
8022
|
+
runTypecheck: true,
|
|
8023
|
+
runBuild: true
|
|
8024
|
+
};
|
|
8025
|
+
}
|
|
8026
|
+
const hasCi = workflows.includes("ci");
|
|
8027
|
+
const hasCd = workflows.includes("cd");
|
|
8028
|
+
let runTests = true;
|
|
8029
|
+
let runLint = true;
|
|
8030
|
+
let runTypecheck = true;
|
|
8031
|
+
let runBuild = true;
|
|
8032
|
+
if (hasCi) {
|
|
8033
|
+
const ciSteps = await checkbox({
|
|
8034
|
+
message: "Which steps should the CI workflow run?",
|
|
8035
|
+
choices: [
|
|
8036
|
+
{ name: "Lint", value: "lint", checked: true },
|
|
8037
|
+
{ name: "Type checking", value: "typecheck", checked: true },
|
|
8038
|
+
{ name: "Tests", value: "tests", checked: true },
|
|
8039
|
+
{ name: "Build", value: "build", checked: true }
|
|
8040
|
+
]
|
|
8041
|
+
});
|
|
8042
|
+
runTests = ciSteps.includes("tests");
|
|
8043
|
+
runLint = ciSteps.includes("lint");
|
|
8044
|
+
runTypecheck = ciSteps.includes("typecheck");
|
|
8045
|
+
runBuild = ciSteps.includes("build");
|
|
8046
|
+
}
|
|
8047
|
+
const nodeVersion = await select({
|
|
8048
|
+
message: "Node.js version:",
|
|
8049
|
+
choices: [
|
|
8050
|
+
{ name: "22 (LTS - Recommended)", value: "22" },
|
|
8051
|
+
{ name: "20 (LTS)", value: "20" },
|
|
8052
|
+
{ name: "18 (LTS)", value: "18" },
|
|
8053
|
+
{ name: "Custom", value: "custom" }
|
|
8054
|
+
],
|
|
8055
|
+
default: "22"
|
|
8056
|
+
});
|
|
8057
|
+
let finalNodeVersion = nodeVersion;
|
|
8058
|
+
if (nodeVersion === "custom") {
|
|
8059
|
+
finalNodeVersion = await input({
|
|
8060
|
+
message: "Enter Node.js version:",
|
|
8061
|
+
default: "22",
|
|
8062
|
+
validate: (v) => {
|
|
8063
|
+
if (!/^\d+(\.\d+)?$/.test(v)) {
|
|
8064
|
+
return 'Please enter a valid version (e.g., "20" or "20.10")';
|
|
8065
|
+
}
|
|
8066
|
+
return true;
|
|
8067
|
+
}
|
|
8068
|
+
});
|
|
8069
|
+
}
|
|
8070
|
+
const enableCaching = await confirm({
|
|
8071
|
+
message: "Enable dependency caching for faster builds?",
|
|
8072
|
+
default: true
|
|
8073
|
+
});
|
|
8074
|
+
return {
|
|
8075
|
+
enabled: true,
|
|
8076
|
+
provider: "github-actions",
|
|
8077
|
+
ci: hasCi,
|
|
8078
|
+
cd: hasCd,
|
|
8079
|
+
packageManager: options?.packageManager || "pnpm",
|
|
8080
|
+
nodeVersion: finalNodeVersion,
|
|
8081
|
+
enableCaching,
|
|
8082
|
+
runTests,
|
|
8083
|
+
runLint,
|
|
8084
|
+
runTypecheck,
|
|
8085
|
+
runBuild
|
|
8086
|
+
};
|
|
8087
|
+
}
|
|
8088
|
+
|
|
7365
8089
|
// src/cli/prompts/folder-preferences.ts
|
|
7366
8090
|
init_esm_shims();
|
|
7367
8091
|
|
|
@@ -7976,18 +8700,38 @@ async function promptProjectInfo(options) {
|
|
|
7976
8700
|
return true;
|
|
7977
8701
|
}
|
|
7978
8702
|
});
|
|
7979
|
-
const
|
|
7980
|
-
message:
|
|
7981
|
-
default: options?.defaults?.
|
|
7982
|
-
validate: (value) => {
|
|
7983
|
-
if (!value.trim()) return "Entity type is required";
|
|
7984
|
-
return true;
|
|
7985
|
-
}
|
|
8703
|
+
const author = await input({
|
|
8704
|
+
message: 'Author (name or "Name <email>"):',
|
|
8705
|
+
default: options?.defaults?.author || ""
|
|
7986
8706
|
});
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
8707
|
+
let entityType = "item";
|
|
8708
|
+
let entityTypePlural = "items";
|
|
8709
|
+
const wantEntityConfig = await confirm({
|
|
8710
|
+
message: "Configure primary entity type? (Used for code examples and templates)",
|
|
8711
|
+
default: false
|
|
7990
8712
|
});
|
|
8713
|
+
if (wantEntityConfig) {
|
|
8714
|
+
logger.info("The entity type is used in code examples and templates throughout the project.");
|
|
8715
|
+
logger.info(
|
|
8716
|
+
'For example, if your project manages "products", code examples will use product-related names.'
|
|
8717
|
+
);
|
|
8718
|
+
logger.newline();
|
|
8719
|
+
entityType = await input({
|
|
8720
|
+
message: "Primary entity type (e.g., product, article, user, listing):",
|
|
8721
|
+
default: options?.defaults?.entityType || "item",
|
|
8722
|
+
validate: (value) => {
|
|
8723
|
+
if (!value.trim()) return "Entity type is required";
|
|
8724
|
+
return true;
|
|
8725
|
+
}
|
|
8726
|
+
});
|
|
8727
|
+
entityTypePlural = await input({
|
|
8728
|
+
message: "Entity type plural:",
|
|
8729
|
+
default: options?.defaults?.entityTypePlural || pluralize(entityType)
|
|
8730
|
+
});
|
|
8731
|
+
} else if (options?.defaults?.entityType) {
|
|
8732
|
+
entityType = options.defaults.entityType;
|
|
8733
|
+
entityTypePlural = options.defaults.entityTypePlural || pluralize(entityType);
|
|
8734
|
+
}
|
|
7991
8735
|
let domain;
|
|
7992
8736
|
let location;
|
|
7993
8737
|
if (!options?.skipOptional) {
|
|
@@ -8026,7 +8770,8 @@ async function promptProjectInfo(options) {
|
|
|
8026
8770
|
domain,
|
|
8027
8771
|
entityType: entityType.trim().toLowerCase(),
|
|
8028
8772
|
entityTypePlural: entityTypePlural.trim().toLowerCase(),
|
|
8029
|
-
location
|
|
8773
|
+
location,
|
|
8774
|
+
author: author.trim() || void 0
|
|
8030
8775
|
};
|
|
8031
8776
|
}
|
|
8032
8777
|
function pluralize(word) {
|
|
@@ -8045,6 +8790,7 @@ async function confirmProjectInfo(info) {
|
|
|
8045
8790
|
logger.keyValue("Name", info.name);
|
|
8046
8791
|
logger.keyValue("Description", info.description);
|
|
8047
8792
|
logger.keyValue("GitHub", `${info.org}/${info.repo}`);
|
|
8793
|
+
if (info.author) logger.keyValue("Author", info.author);
|
|
8048
8794
|
logger.keyValue("Entity", `${info.entityType} / ${info.entityTypePlural}`);
|
|
8049
8795
|
if (info.domain) logger.keyValue("Domain", info.domain);
|
|
8050
8796
|
if (info.location) logger.keyValue("Location", info.location);
|
|
@@ -9993,6 +10739,17 @@ function placeholderKeyToConfigKey(key, category) {
|
|
|
9993
10739
|
async function promptTemplateConfig(options) {
|
|
9994
10740
|
const { context, mode = "quick", category, requiredOnly = false } = options;
|
|
9995
10741
|
logger.section("Template Configuration", "\u{1F4DD}");
|
|
10742
|
+
logger.newline();
|
|
10743
|
+
logger.info("This step personalizes all Claude Code configuration files for YOUR project.");
|
|
10744
|
+
logger.info("Your answers will be used to:");
|
|
10745
|
+
logger.newline();
|
|
10746
|
+
logger.item("Replace {{PLACEHOLDERS}} in agents, commands, and skills");
|
|
10747
|
+
logger.item("Configure CLAUDE.md with your tech stack and commands");
|
|
10748
|
+
logger.item("Set up quality targets (coverage, performance, accessibility)");
|
|
10749
|
+
logger.item("Customize code examples to match your project structure");
|
|
10750
|
+
logger.newline();
|
|
10751
|
+
logger.info("Accurate configuration means better AI assistance tailored to your codebase!");
|
|
10752
|
+
logger.newline();
|
|
9996
10753
|
const hasDefaults = await hasGlobalDefaults();
|
|
9997
10754
|
const globalDefaults = hasDefaults ? await getGlobalTemplateConfig() : void 0;
|
|
9998
10755
|
if (mode === "quick") {
|
|
@@ -10265,7 +11022,7 @@ async function runInit(path8, options) {
|
|
|
10265
11022
|
logger.warn("Configuration cancelled");
|
|
10266
11023
|
return;
|
|
10267
11024
|
}
|
|
10268
|
-
const { config, skippedMcpConfigs, templateConfig } = buildResult;
|
|
11025
|
+
const { config, skippedMcpConfigs, templateConfig, cicdConfig } = buildResult;
|
|
10269
11026
|
if (templateConfig) {
|
|
10270
11027
|
config.templateConfig = templateConfig;
|
|
10271
11028
|
}
|
|
@@ -10282,7 +11039,7 @@ async function runInit(path8, options) {
|
|
|
10282
11039
|
showConfigSummary(config);
|
|
10283
11040
|
return;
|
|
10284
11041
|
}
|
|
10285
|
-
await executeInstallation(projectPath, config, registry, templatesPath, options);
|
|
11042
|
+
await executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig);
|
|
10286
11043
|
if (templateConfig && !options.noPlaceholders) {
|
|
10287
11044
|
const claudePath = joinPath(projectPath, ".claude");
|
|
10288
11045
|
await replaceTemplateConfigWithSpinner(claudePath, templateConfig);
|
|
@@ -10462,6 +11219,9 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
|
|
|
10462
11219
|
}
|
|
10463
11220
|
const permissionsConfig = await promptPermissionsConfig();
|
|
10464
11221
|
const codeStyleConfig = await promptCodeStyleConfig();
|
|
11222
|
+
const cicdConfig = await promptCICDConfig({
|
|
11223
|
+
packageManager: preferences.packageManager
|
|
11224
|
+
});
|
|
10465
11225
|
const folderPreferences = await promptQuickFolderPreferences({
|
|
10466
11226
|
selectedBundles: bundleSelection.selectedBundles,
|
|
10467
11227
|
technologies: detection.detectedTechnologies || []
|
|
@@ -10519,7 +11279,8 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
|
|
|
10519
11279
|
return {
|
|
10520
11280
|
config,
|
|
10521
11281
|
skippedMcpConfigs,
|
|
10522
|
-
templateConfig: templateConfigResult
|
|
11282
|
+
templateConfig: templateConfigResult,
|
|
11283
|
+
cicdConfig
|
|
10523
11284
|
};
|
|
10524
11285
|
}
|
|
10525
11286
|
async function selectModulesWithBundles(registry, suggestedBundles) {
|
|
@@ -10567,7 +11328,7 @@ async function selectModulesWithBundles(registry, suggestedBundles) {
|
|
|
10567
11328
|
}
|
|
10568
11329
|
return result;
|
|
10569
11330
|
}
|
|
10570
|
-
async function executeInstallation(projectPath, config, registry, templatesPath, options) {
|
|
11331
|
+
async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig) {
|
|
10571
11332
|
logger.newline();
|
|
10572
11333
|
logger.title("Installing Configuration");
|
|
10573
11334
|
if (config.scaffold.type === "full-project") {
|
|
@@ -10579,6 +11340,16 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
|
|
|
10579
11340
|
...scaffoldResult.createdFiles
|
|
10580
11341
|
];
|
|
10581
11342
|
}
|
|
11343
|
+
const claudeMdResult = await generateClaudeMdWithSpinner(projectPath, config.project, {
|
|
11344
|
+
overwrite: options.force,
|
|
11345
|
+
templateConfig: config.templateConfig,
|
|
11346
|
+
claudeConfig: config
|
|
11347
|
+
});
|
|
11348
|
+
if (claudeMdResult.error) {
|
|
11349
|
+
logger.warn(`CLAUDE.md generation warning: ${claudeMdResult.error}`);
|
|
11350
|
+
} else if (claudeMdResult.skipped) {
|
|
11351
|
+
logger.info("CLAUDE.md already exists, skipped");
|
|
11352
|
+
}
|
|
10582
11353
|
const modulesByCategory = {
|
|
10583
11354
|
agents: filterModules(registry, "agents", config.modules.agents.selected),
|
|
10584
11355
|
skills: filterModules(registry, "skills", config.modules.skills.selected),
|
|
@@ -10639,6 +11410,33 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
|
|
|
10639
11410
|
if (codeStyleResult.errors.length > 0) {
|
|
10640
11411
|
logger.warn(`Code style installation warnings: ${codeStyleResult.errors.join(", ")}`);
|
|
10641
11412
|
}
|
|
11413
|
+
const vscodeResult = await installVSCodeConfig(projectPath, config.extras.codeStyle, {
|
|
11414
|
+
overwrite: options.force,
|
|
11415
|
+
merge: !options.force
|
|
11416
|
+
// Merge with existing settings if not forcing
|
|
11417
|
+
});
|
|
11418
|
+
if (vscodeResult.settings.error) {
|
|
11419
|
+
logger.warn(`VSCode settings warning: ${vscodeResult.settings.error}`);
|
|
11420
|
+
} else if (vscodeResult.settings.created || vscodeResult.settings.updated) {
|
|
11421
|
+
logger.info("VSCode settings configured for code style tools");
|
|
11422
|
+
}
|
|
11423
|
+
const huskyConfig = deriveHuskyConfigFromCodeStyle(config.extras.codeStyle);
|
|
11424
|
+
if (huskyConfig) {
|
|
11425
|
+
const huskyResult = await installHuskyWithSpinner(projectPath, huskyConfig, {
|
|
11426
|
+
overwrite: options.force
|
|
11427
|
+
});
|
|
11428
|
+
if (huskyResult.errors.length > 0) {
|
|
11429
|
+
logger.warn(`Husky installation warnings: ${huskyResult.errors.join(", ")}`);
|
|
11430
|
+
}
|
|
11431
|
+
}
|
|
11432
|
+
}
|
|
11433
|
+
if (cicdConfig?.enabled) {
|
|
11434
|
+
const cicdResult = await installCICDWithSpinner(projectPath, cicdConfig, {
|
|
11435
|
+
overwrite: options.force
|
|
11436
|
+
});
|
|
11437
|
+
if (cicdResult.errors.length > 0) {
|
|
11438
|
+
logger.warn(`CI/CD installation warnings: ${cicdResult.errors.join(", ")}`);
|
|
11439
|
+
}
|
|
10642
11440
|
}
|
|
10643
11441
|
await writeConfig(projectPath, config);
|
|
10644
11442
|
logger.newline();
|
|
@@ -10667,6 +11465,7 @@ async function handlePackageJsonUpdate(projectPath, config, options) {
|
|
|
10667
11465
|
project: {
|
|
10668
11466
|
name: config.project.name,
|
|
10669
11467
|
description: config.project.description,
|
|
11468
|
+
author: config.project.author,
|
|
10670
11469
|
repository: config.project.org && config.project.repo ? `https://github.com/${config.project.org}/${config.project.repo}` : void 0
|
|
10671
11470
|
}
|
|
10672
11471
|
};
|
|
@@ -10736,7 +11535,7 @@ async function handlePackageJsonUpdate(projectPath, config, options) {
|
|
|
10736
11535
|
logger.newline();
|
|
10737
11536
|
logger.subtitle("Next Steps");
|
|
10738
11537
|
logger.info("Run the following command to install dependencies:");
|
|
10739
|
-
logger.raw(` ${
|
|
11538
|
+
logger.raw(` ${getInstallCommand2(packageManager)}`);
|
|
10740
11539
|
const setupInstructions = getSetupInstructions(toolSelection);
|
|
10741
11540
|
if (setupInstructions.length > 0) {
|
|
10742
11541
|
logger.newline();
|