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