@qazuor/claude-code-config 0.6.1 → 0.6.3

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
@@ -5523,12 +5523,13 @@ var DEFAULT_DENY_RULES = [
5523
5523
  "Write(/sbin/**)",
5524
5524
  "Write(/var/**)",
5525
5525
  "Write(/tmp/**)",
5526
- // Dangerous commands
5526
+ // Dangerous commands - use exact matches or :* prefix
5527
5527
  "Bash(rm -rf /)",
5528
- "Bash(sudo *)",
5529
- "Bash(chmod 777 *)",
5530
- "Bash(curl * | bash)",
5531
- "Bash(wget * | bash)",
5528
+ "Bash(sudo:*)",
5529
+ "Bash(chmod 777:*)",
5530
+ // Note: Can't deny "curl * | bash" pattern - use specific denies instead
5531
+ "Bash(curl:*)",
5532
+ "Bash(wget:*)",
5532
5533
  // Sensitive files
5533
5534
  "Write(.env)",
5534
5535
  "Write(.env.*)",
@@ -5594,64 +5595,69 @@ function generateAllowRules(config) {
5594
5595
  }
5595
5596
  if (config.git.readOnly) {
5596
5597
  rules.push(
5597
- "Bash(git status*)",
5598
- "Bash(git diff*)",
5599
- "Bash(git log*)",
5600
- "Bash(git show*)",
5601
- "Bash(git branch*)"
5598
+ "Bash(git status:*)",
5599
+ "Bash(git diff:*)",
5600
+ "Bash(git log:*)",
5601
+ "Bash(git show:*)",
5602
+ "Bash(git branch:*)"
5602
5603
  );
5603
5604
  }
5604
5605
  if (config.git.staging) {
5605
- rules.push("Bash(git add*)");
5606
+ rules.push("Bash(git add:*)");
5606
5607
  }
5607
5608
  if (config.git.commit) {
5608
- rules.push("Bash(git commit*)");
5609
+ rules.push("Bash(git commit:*)");
5609
5610
  }
5610
5611
  if (config.git.push) {
5611
- rules.push("Bash(git push*)");
5612
+ rules.push("Bash(git push:*)");
5612
5613
  }
5613
5614
  if (config.git.branching) {
5614
- rules.push("Bash(git checkout*)", "Bash(git branch*)", "Bash(git merge*)", "Bash(git rebase*)");
5615
+ rules.push(
5616
+ "Bash(git checkout:*)",
5617
+ "Bash(git branch:*)",
5618
+ "Bash(git merge:*)",
5619
+ "Bash(git rebase:*)"
5620
+ );
5615
5621
  }
5616
5622
  if (config.bash.packageManager) {
5617
5623
  rules.push(
5618
- "Bash(pnpm *)",
5619
- "Bash(npm *)",
5620
- "Bash(yarn *)",
5621
- "Bash(bun *)",
5622
- "Bash(npx *)",
5623
- "Bash(bunx *)"
5624
+ "Bash(pnpm:*)",
5625
+ "Bash(npm:*)",
5626
+ "Bash(yarn:*)",
5627
+ "Bash(bun:*)",
5628
+ "Bash(npx:*)",
5629
+ "Bash(bunx:*)"
5624
5630
  );
5625
5631
  }
5626
5632
  if (config.bash.testing) {
5627
5633
  rules.push(
5628
- "Bash(vitest*)",
5629
- "Bash(jest*)",
5630
- "Bash(playwright*)",
5631
- "Bash(cypress*)",
5632
- "Bash(pnpm test*)",
5633
- "Bash(npm test*)",
5634
- "Bash(pnpm run test*)",
5635
- "Bash(npm run test*)"
5634
+ "Bash(vitest:*)",
5635
+ "Bash(jest:*)",
5636
+ "Bash(playwright:*)",
5637
+ "Bash(cypress:*)",
5638
+ "Bash(pnpm test:*)",
5639
+ "Bash(npm test:*)",
5640
+ "Bash(pnpm run test:*)",
5641
+ "Bash(npm run test:*)"
5636
5642
  );
5637
5643
  }
5638
5644
  if (config.bash.building) {
5639
5645
  rules.push(
5640
- "Bash(pnpm build*)",
5641
- "Bash(npm run build*)",
5642
- "Bash(pnpm run build*)",
5643
- "Bash(tsc*)",
5644
- "Bash(tsup*)",
5645
- "Bash(vite build*)",
5646
- "Bash(next build*)",
5647
- "Bash(astro build*)"
5646
+ "Bash(pnpm build:*)",
5647
+ "Bash(npm run build:*)",
5648
+ "Bash(pnpm run build:*)",
5649
+ "Bash(tsc:*)",
5650
+ "Bash(tsup:*)",
5651
+ "Bash(vite build:*)",
5652
+ "Bash(next build:*)",
5653
+ "Bash(astro build:*)"
5648
5654
  );
5649
5655
  }
5650
5656
  if (config.bash.docker) {
5651
- rules.push("Bash(docker *)", "Bash(docker-compose *)");
5657
+ rules.push("Bash(docker:*)", "Bash(docker-compose:*)");
5652
5658
  }
5653
5659
  if (config.bash.arbitrary) {
5654
- rules.push("Bash(*)");
5660
+ rules.push("Bash");
5655
5661
  }
5656
5662
  if (config.web.fetch) {
5657
5663
  rules.push("WebFetch");
@@ -6065,29 +6071,6 @@ function showReplacementReport(report) {
6065
6071
  // src/lib/scaffold/claude-md-generator.ts
6066
6072
  init_cjs_shims();
6067
6073
  init_fs();
6068
-
6069
- // src/lib/utils/paths.ts
6070
- init_cjs_shims();
6071
- var import_node_fs = __toESM(require("fs"), 1);
6072
- var import_node_path3 = __toESM(require("path"), 1);
6073
- var import_node_url = require("url");
6074
- function getPackageRoot() {
6075
- const currentFilePath = (0, import_node_url.fileURLToPath)(importMetaUrl);
6076
- let currentDir = import_node_path3.default.dirname(currentFilePath);
6077
- while (currentDir !== import_node_path3.default.dirname(currentDir)) {
6078
- const packageJsonPath = import_node_path3.default.join(currentDir, "package.json");
6079
- if (import_node_fs.default.existsSync(packageJsonPath)) {
6080
- return currentDir;
6081
- }
6082
- currentDir = import_node_path3.default.dirname(currentDir);
6083
- }
6084
- throw new Error("Could not find package root (no package.json found in parent directories)");
6085
- }
6086
- function getTemplatesPath() {
6087
- return import_node_path3.default.join(getPackageRoot(), "templates");
6088
- }
6089
-
6090
- // src/lib/scaffold/claude-md-generator.ts
6091
6074
  async function generateClaudeMd(projectPath, projectInfo, options) {
6092
6075
  const claudeMdPath = joinPath(projectPath, "CLAUDE.md");
6093
6076
  const exists = await pathExists(claudeMdPath);
@@ -6099,18 +6082,12 @@ async function generateClaudeMd(projectPath, projectInfo, options) {
6099
6082
  };
6100
6083
  }
6101
6084
  try {
6102
- let template;
6085
+ let content;
6103
6086
  if (options?.customTemplate) {
6104
- template = options.customTemplate;
6087
+ content = processCustomTemplate(options.customTemplate, projectInfo);
6105
6088
  } else {
6106
- const templatePath = joinPath(getTemplatesPath(), "CLAUDE.md.template");
6107
- if (await pathExists(templatePath)) {
6108
- template = await readFile(templatePath);
6109
- } else {
6110
- template = getMinimalTemplate();
6111
- }
6089
+ content = generateClaudeMdContent(projectInfo, options);
6112
6090
  }
6113
- const content = processTemplate(template, projectInfo, options);
6114
6091
  await writeFile(claudeMdPath, content);
6115
6092
  return {
6116
6093
  created: true,
@@ -6135,41 +6112,165 @@ async function generateClaudeMdWithSpinner(projectPath, projectInfo, options) {
6135
6112
  }
6136
6113
  );
6137
6114
  }
6138
- function processTemplate(template, projectInfo, options) {
6139
- let content = template;
6115
+ function processCustomTemplate(template, projectInfo) {
6116
+ return template.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(/\{\{DOMAIN\}\}/g, projectInfo.domain || "").replace(/\{\{LOCATION\}\}/g, projectInfo.location || "");
6117
+ }
6118
+ function generateClaudeMdContent(projectInfo, options) {
6140
6119
  const techStack = options?.templateConfig?.techStack;
6141
6120
  const commands = options?.templateConfig?.commands;
6142
6121
  const targets = options?.templateConfig?.targets;
6143
6122
  const preferences = options?.claudeConfig?.preferences;
6144
6123
  const standards = options?.claudeConfig?.extras?.standards;
6145
- 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 || "");
6146
6124
  const packageManager = preferences?.packageManager || "pnpm";
6147
- content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
6148
- const coverageTarget = standards?.testing?.coverageTarget ? String(standards.testing.coverageTarget) : targets && "coverage" in targets ? String(targets.coverage) : "90";
6149
- content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
6150
- content = processStandardsPlaceholders(content, standards, preferences);
6125
+ const lines = [];
6126
+ lines.push("# CLAUDE.md");
6127
+ lines.push("");
6128
+ lines.push("## Project Overview");
6129
+ lines.push("");
6130
+ lines.push(`**${projectInfo.name}** - ${projectInfo.description}`);
6131
+ lines.push("");
6132
+ lines.push("## Repository");
6133
+ lines.push("");
6134
+ lines.push(`- **Organization:** ${projectInfo.org}`);
6135
+ lines.push(`- **Repository:** ${projectInfo.repo}`);
6136
+ lines.push(`- **GitHub:** https://github.com/${projectInfo.org}/${projectInfo.repo}`);
6151
6137
  if (projectInfo.domain) {
6152
- content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
6153
- } else {
6154
- content = content.replace(/\{\{#if DOMAIN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6138
+ lines.push(`- **Domain:** ${projectInfo.domain}`);
6155
6139
  }
6156
- if (techStack && Object.keys(techStack).length > 0) {
6157
- const techStackContent = generateTechStackSection(techStack);
6158
- content = content.replace(/\{\{#if TECH_STACK\}\}/g, "").replace(/\{\{TECH_STACK\}\}\n\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, techStackContent);
6159
- } else {
6160
- content = content.replace(/\{\{#if TECH_STACK\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6140
+ lines.push("");
6141
+ lines.push("## Tech Stack");
6142
+ lines.push("");
6143
+ lines.push(generateTechStackSection(techStack));
6144
+ lines.push("## Project Structure");
6145
+ lines.push("");
6146
+ lines.push("```text");
6147
+ lines.push(`${projectInfo.name}/`);
6148
+ lines.push("\u251C\u2500\u2500 src/ # Source code");
6149
+ lines.push("\u251C\u2500\u2500 tests/ # Test files");
6150
+ lines.push("\u251C\u2500\u2500 docs/ # Documentation");
6151
+ lines.push("\u2514\u2500\u2500 .claude/ # Claude configuration");
6152
+ lines.push(" \u251C\u2500\u2500 agents/ # AI agent definitions");
6153
+ lines.push(" \u251C\u2500\u2500 commands/ # Custom slash commands");
6154
+ lines.push(" \u251C\u2500\u2500 skills/ # Specialized skills");
6155
+ lines.push(" \u2514\u2500\u2500 docs/ # AI-specific documentation");
6156
+ lines.push("```");
6157
+ lines.push("");
6158
+ lines.push("## Quick Commands");
6159
+ lines.push("");
6160
+ lines.push(generateCommandsSection(commands, packageManager));
6161
+ lines.push("## Development Guidelines");
6162
+ lines.push("");
6163
+ lines.push("### Code Standards");
6164
+ lines.push("");
6165
+ lines.push("- Primary language: TypeScript");
6166
+ lines.push("- Follow TypeScript best practices");
6167
+ if (standards?.code?.namedExportsOnly) {
6168
+ lines.push("- Use named exports only (no default exports)");
6169
+ }
6170
+ const maxLines = standards?.code?.maxFileLines || 500;
6171
+ lines.push(`- Maximum ${maxLines} lines per file`);
6172
+ if (standards?.code?.jsDocRequired) {
6173
+ lines.push("- Document all exports with JSDoc");
6174
+ }
6175
+ if (standards?.code?.roroPattern) {
6176
+ lines.push("- Use RO-RO pattern (Receive Object, Return Object)");
6161
6177
  }
6162
- if (commands && Object.keys(commands).length > 0) {
6163
- const commandsContent = generateCommandsSection(commands, packageManager);
6164
- content = content.replace(/\{\{#if COMMANDS\}\}/g, "").replace(/\{\{COMMANDS\}\}\n\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, commandsContent);
6178
+ lines.push("");
6179
+ lines.push("### Testing");
6180
+ lines.push("");
6181
+ if (standards?.testing?.tddRequired) {
6182
+ lines.push("- Methodology: TDD (Test-Driven Development)");
6183
+ lines.push("- Write tests first: Red -> Green -> Refactor");
6165
6184
  } else {
6166
- content = content.replace(/\{\{#if COMMANDS\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6185
+ lines.push("- Write comprehensive tests for all features");
6186
+ }
6187
+ const coverageTarget = standards?.testing?.coverageTarget || (targets && "coverage" in targets ? targets.coverage : 90);
6188
+ lines.push(`- Maintain ${coverageTarget}%+ code coverage`);
6189
+ const testPattern = standards?.testing?.testPattern === "gwt" ? "GWT (Given-When-Then)" : "AAA (Arrange, Act, Assert)";
6190
+ lines.push(`- Test pattern: ${testPattern}`);
6191
+ if (standards?.testing?.testLocation) {
6192
+ const testLocationText = standards.testing.testLocation === "colocated" ? "Co-located with source" : "Separate test directory";
6193
+ lines.push(`- Test location: ${testLocationText}`);
6194
+ }
6195
+ lines.push("");
6196
+ lines.push("### Git Workflow");
6197
+ lines.push("");
6198
+ lines.push("- Use conventional commits: `type(scope): description`");
6199
+ lines.push("- Types: feat, fix, docs, style, refactor, test, chore");
6200
+ lines.push("- Keep commits atomic and focused");
6201
+ if (preferences?.includeCoAuthor) {
6202
+ lines.push("");
6203
+ lines.push("#### Commit Attribution");
6204
+ lines.push("");
6205
+ lines.push("Include the following in commit messages:");
6206
+ lines.push("```");
6207
+ lines.push("\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)");
6208
+ lines.push("");
6209
+ lines.push("Co-Authored-By: Claude <noreply@anthropic.com>");
6210
+ lines.push("```");
6167
6211
  }
6168
- content = content.replace(/\{\{#if PROJECT_STRUCTURE\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6169
- return content;
6212
+ lines.push("");
6213
+ lines.push("## Claude Behavior Guidelines");
6214
+ lines.push("");
6215
+ lines.push("### Critical Thinking");
6216
+ lines.push("");
6217
+ lines.push("- You are an expert who double-checks things, you are skeptical and do research");
6218
+ lines.push("- Neither the user nor you are always right, but both strive for accuracy");
6219
+ lines.push("- When the user asks something, reason with these questions:");
6220
+ lines.push(' - "Why might the user be wrong?"');
6221
+ lines.push(' - "What arguments exist against what the user thinks?"');
6222
+ lines.push(` - "Act as devil's advocate - why might this proposal fail?"`);
6223
+ lines.push(` - "Imagine you're in a debate - how would you refute this?"`);
6224
+ lines.push(
6225
+ "- Always ask to better understand the context of the requested change before implementing"
6226
+ );
6227
+ lines.push("");
6228
+ lines.push("### Communication Style");
6229
+ lines.push("");
6230
+ if (preferences?.responseLanguage) {
6231
+ const langDisplay = preferences.responseLanguage === "es" ? "Spanish" : preferences.responseLanguage === "en" ? "English" : preferences.responseLanguage;
6232
+ lines.push(`- Respond in ${langDisplay}`);
6233
+ }
6234
+ lines.push("- Code and comments should always be in English");
6235
+ lines.push("- Be direct and concise");
6236
+ lines.push('- Explain the "why" behind decisions when relevant');
6237
+ lines.push("");
6238
+ lines.push("### Writing Style");
6239
+ lines.push("");
6240
+ lines.push(
6241
+ '- Systematically replace em-dashes ("\u2014") with a period (".") to start a new sentence, or a comma (",") to continue the sentence'
6242
+ );
6243
+ lines.push("- Avoid unnecessary filler words");
6244
+ lines.push("- Use clear, technical language");
6245
+ lines.push("");
6246
+ lines.push("## Claude Configuration");
6247
+ lines.push("");
6248
+ lines.push("This project uses `@qazuor/claude-code-config` for AI-assisted development.");
6249
+ lines.push("");
6250
+ lines.push("### Available Commands");
6251
+ lines.push("");
6252
+ lines.push("Run `/help` in Claude to see all available commands.");
6253
+ lines.push("");
6254
+ lines.push("### Documentation");
6255
+ lines.push("");
6256
+ lines.push("- Quick Start: `.claude/docs/quick-start.md`");
6257
+ lines.push("- Workflows: `.claude/docs/workflows/README.md`");
6258
+ lines.push("- Standards: `.claude/docs/standards/`");
6259
+ lines.push("");
6260
+ lines.push("---");
6261
+ lines.push("");
6262
+ lines.push(
6263
+ "*Generated by [@qazuor/claude-code-config](https://github.com/qazuor/claude-code-config)*"
6264
+ );
6265
+ lines.push("");
6266
+ return lines.join("\n");
6170
6267
  }
6171
6268
  function generateTechStackSection(techStack) {
6269
+ if (!techStack) {
6270
+ return getDefaultTechStack();
6271
+ }
6172
6272
  const lines = [];
6273
+ let hasContent = false;
6173
6274
  if (techStack.frontendFramework && techStack.frontendFramework !== "None") {
6174
6275
  lines.push("**Frontend:**");
6175
6276
  lines.push(`- Framework: ${techStack.frontendFramework}`);
@@ -6177,6 +6278,7 @@ function generateTechStackSection(techStack) {
6177
6278
  lines.push(`- State: ${techStack.stateManagement}`);
6178
6279
  }
6179
6280
  lines.push("");
6281
+ hasContent = true;
6180
6282
  }
6181
6283
  if (techStack.apiFramework && techStack.apiFramework !== "None") {
6182
6284
  lines.push("**Backend:**");
@@ -6185,145 +6287,89 @@ function generateTechStackSection(techStack) {
6185
6287
  lines.push(`- Validation: ${techStack.validationLibrary}`);
6186
6288
  }
6187
6289
  lines.push("");
6290
+ hasContent = true;
6188
6291
  }
6189
6292
  if (techStack.databaseOrm && techStack.databaseOrm !== "None") {
6190
6293
  lines.push("**Database:**");
6191
6294
  lines.push(`- ORM: ${techStack.databaseOrm}`);
6192
6295
  lines.push("");
6296
+ hasContent = true;
6193
6297
  }
6194
6298
  if (techStack.authPattern && techStack.authPattern !== "None") {
6195
6299
  lines.push("**Authentication:**");
6196
6300
  lines.push(`- Provider: ${techStack.authPattern}`);
6197
6301
  lines.push("");
6302
+ hasContent = true;
6198
6303
  }
6199
6304
  if (techStack.testFramework && techStack.testFramework !== "None") {
6200
6305
  lines.push("**Testing:**");
6201
6306
  lines.push(`- Framework: ${techStack.testFramework}`);
6202
6307
  lines.push("");
6308
+ hasContent = true;
6203
6309
  }
6204
6310
  if (techStack.bundler && techStack.bundler !== "None") {
6205
6311
  lines.push("**Build:**");
6206
6312
  lines.push(`- Bundler: ${techStack.bundler}`);
6207
6313
  lines.push("");
6314
+ hasContent = true;
6315
+ }
6316
+ if (!hasContent) {
6317
+ return getDefaultTechStack();
6208
6318
  }
6209
6319
  return lines.join("\n");
6210
6320
  }
6211
- function generateCommandsSection(commands, packageManager) {
6321
+ function getDefaultTechStack() {
6322
+ return `**Frontend:**
6323
+ - Framework: Not configured
6324
+
6325
+ **Backend:**
6326
+ - API: Not configured
6327
+
6328
+ **Database:**
6329
+ - ORM: Not configured
6330
+
6331
+ **Testing:**
6332
+ - Framework: Not configured
6333
+
6334
+ `;
6335
+ }
6336
+ function generateCommandsSection(commands, packageManager = "pnpm") {
6212
6337
  const lines = ["```bash"];
6213
6338
  lines.push("# Development");
6214
6339
  lines.push(`${packageManager} dev # Start development server`);
6215
6340
  lines.push("");
6216
6341
  lines.push("# Testing");
6217
- if (commands.test) {
6342
+ if (commands?.test) {
6218
6343
  lines.push(`${commands.test} # Run tests`);
6219
6344
  } else {
6220
6345
  lines.push(`${packageManager} test # Run tests`);
6221
6346
  }
6222
- if (commands.coverage) {
6347
+ if (commands?.coverage) {
6223
6348
  lines.push(`${commands.coverage} # Run tests with coverage`);
6224
6349
  } else {
6225
6350
  lines.push(`${packageManager} test:coverage # Run tests with coverage`);
6226
6351
  }
6227
6352
  lines.push("");
6228
6353
  lines.push("# Quality");
6229
- if (commands.lint) {
6354
+ if (commands?.lint) {
6230
6355
  lines.push(`${commands.lint} # Run linter`);
6231
6356
  } else {
6232
6357
  lines.push(`${packageManager} lint # Run linter`);
6233
6358
  }
6234
- if (commands.typecheck) {
6359
+ if (commands?.typecheck) {
6235
6360
  lines.push(`${commands.typecheck} # Type checking`);
6236
6361
  } else {
6237
6362
  lines.push(`${packageManager} typecheck # Type checking`);
6238
6363
  }
6239
- if (commands.build) {
6364
+ if (commands?.build) {
6240
6365
  lines.push("");
6241
6366
  lines.push("# Build");
6242
6367
  lines.push(`${commands.build} # Build for production`);
6243
6368
  }
6244
6369
  lines.push("```");
6370
+ lines.push("");
6245
6371
  return lines.join("\n");
6246
6372
  }
6247
- function processStandardsPlaceholders(content, standards, preferences) {
6248
- let result = content;
6249
- const primaryLanguage = "TypeScript";
6250
- result = result.replace(/\{\{PRIMARY_LANGUAGE\}\}/g, primaryLanguage);
6251
- const maxFileLines = standards?.code?.maxFileLines?.toString() || "500";
6252
- result = result.replace(/\{\{MAX_FILE_LINES\}\}/g, maxFileLines);
6253
- const testPattern = standards?.testing?.testPattern === "gwt" ? "GWT (Given-When-Then)" : "AAA (Arrange, Act, Assert)";
6254
- result = result.replace(/\{\{TEST_PATTERN\}\}/g, testPattern);
6255
- const responseLanguage = preferences?.responseLanguage === "es" ? "Spanish" : preferences?.responseLanguage === "en" ? "English" : "Spanish";
6256
- result = result.replace(/\{\{RESPONSE_LANGUAGE\}\}/g, responseLanguage);
6257
- if (standards?.code?.namedExportsOnly) {
6258
- result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6259
- } else {
6260
- result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6261
- }
6262
- if (standards?.code?.jsDocRequired) {
6263
- result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6264
- } else {
6265
- result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6266
- }
6267
- if (standards?.code?.roroPattern) {
6268
- result = result.replace(/\{\{#if RORO_PATTERN\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6269
- } else {
6270
- result = result.replace(/\{\{#if RORO_PATTERN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6271
- }
6272
- if (standards?.testing?.tddRequired) {
6273
- result = result.replace(/\{\{#if TDD_REQUIRED\}\}/g, "").replace(/\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6274
- } else {
6275
- result = result.replace(/\{\{#if TDD_REQUIRED\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6276
- }
6277
- const testLocation = standards?.testing?.testLocation;
6278
- if (testLocation) {
6279
- const testLocationText = testLocation === "colocated" ? "Co-located with source" : "Separate test directory";
6280
- result = result.replace(/\{\{#if TEST_LOCATION\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{TEST_LOCATION\}\}/g, testLocationText);
6281
- } else {
6282
- result = result.replace(/\{\{#if TEST_LOCATION\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6283
- }
6284
- if (preferences?.includeCoAuthor) {
6285
- result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6286
- } else {
6287
- result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6288
- }
6289
- if (preferences?.responseLanguage) {
6290
- result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6291
- } else {
6292
- result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6293
- }
6294
- return result;
6295
- }
6296
- function getMinimalTemplate() {
6297
- return `# CLAUDE.md
6298
-
6299
- ## Project Overview
6300
-
6301
- **{{PROJECT_NAME}}** - {{PROJECT_DESCRIPTION}}
6302
-
6303
- ## Repository
6304
-
6305
- - **GitHub:** https://github.com/{{ORG}}/{{REPO}}
6306
-
6307
- ## Quick Commands
6308
-
6309
- \`\`\`bash
6310
- {{PACKAGE_MANAGER}} dev # Start development
6311
- {{PACKAGE_MANAGER}} test # Run tests
6312
- {{PACKAGE_MANAGER}} lint # Run linter
6313
- {{PACKAGE_MANAGER}} build # Build project
6314
- \`\`\`
6315
-
6316
- ## Claude Configuration
6317
-
6318
- This project uses \`@qazuor/claude-code-config\` for AI-assisted development.
6319
-
6320
- See \`.claude/docs/quick-start.md\` for getting started.
6321
-
6322
- ---
6323
-
6324
- *Generated by [@qazuor/claude-code-config](https://github.com/qazuor/claude-code-config)*
6325
- `;
6326
- }
6327
6373
 
6328
6374
  // src/lib/scaffold/index.ts
6329
6375
  init_cjs_shims();
@@ -7076,8 +7122,8 @@ function buildSettingsLocalJson(options) {
7076
7122
 
7077
7123
  // src/lib/templates/config-replacer.ts
7078
7124
  init_cjs_shims();
7079
- var fs4 = __toESM(require("fs/promises"), 1);
7080
- var path5 = __toESM(require("path"), 1);
7125
+ var fs3 = __toESM(require("fs/promises"), 1);
7126
+ var path4 = __toESM(require("path"), 1);
7081
7127
  var import_ora2 = __toESM(require("ora"), 1);
7082
7128
 
7083
7129
  // src/constants/template-placeholders.ts
@@ -7985,7 +8031,7 @@ function flattenTemplateConfig(config) {
7985
8031
  return flattened;
7986
8032
  }
7987
8033
  function shouldProcessFile(filePath) {
7988
- const ext = path5.extname(filePath).toLowerCase();
8034
+ const ext = path4.extname(filePath).toLowerCase();
7989
8035
  return PROCESSABLE_EXTENSIONS.includes(ext);
7990
8036
  }
7991
8037
  function shouldSkipDirectory(dirName) {
@@ -7994,9 +8040,9 @@ function shouldSkipDirectory(dirName) {
7994
8040
  async function getAllFiles(dir) {
7995
8041
  const files = [];
7996
8042
  try {
7997
- const entries = await fs4.readdir(dir, { withFileTypes: true });
8043
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
7998
8044
  for (const entry of entries) {
7999
- const fullPath = path5.join(dir, entry.name);
8045
+ const fullPath = path4.join(dir, entry.name);
8000
8046
  if (entry.isDirectory()) {
8001
8047
  if (!shouldSkipDirectory(entry.name)) {
8002
8048
  const subFiles = await getAllFiles(fullPath);
@@ -8013,7 +8059,7 @@ async function getAllFiles(dir) {
8013
8059
  async function replaceInFile2(filePath, replacements) {
8014
8060
  const changes = [];
8015
8061
  try {
8016
- let content = await fs4.readFile(filePath, "utf-8");
8062
+ let content = await fs3.readFile(filePath, "utf-8");
8017
8063
  let modified = false;
8018
8064
  for (const [placeholder, value] of Object.entries(replacements)) {
8019
8065
  if (content.includes(placeholder)) {
@@ -8023,7 +8069,7 @@ async function replaceInFile2(filePath, replacements) {
8023
8069
  }
8024
8070
  }
8025
8071
  if (modified) {
8026
- await fs4.writeFile(filePath, content, "utf-8");
8072
+ await fs3.writeFile(filePath, content, "utf-8");
8027
8073
  }
8028
8074
  } catch {
8029
8075
  }
@@ -8044,7 +8090,7 @@ async function replaceTemplatePlaceholders(dir, config) {
8044
8090
  report.filesModified++;
8045
8091
  for (const change of changes) {
8046
8092
  report.replacements.push({
8047
- file: path5.relative(dir, file),
8093
+ file: path4.relative(dir, file),
8048
8094
  placeholder: change.placeholder,
8049
8095
  value: change.value
8050
8096
  });
@@ -8115,11 +8161,11 @@ async function previewReplacements(dir, config) {
8115
8161
  const preview = [];
8116
8162
  for (const file of files) {
8117
8163
  try {
8118
- const content = await fs4.readFile(file, "utf-8");
8164
+ const content = await fs3.readFile(file, "utf-8");
8119
8165
  for (const [placeholder, value] of Object.entries(replacements)) {
8120
8166
  if (content.includes(placeholder)) {
8121
8167
  preview.push({
8122
- file: path5.relative(dir, file),
8168
+ file: path4.relative(dir, file),
8123
8169
  placeholder,
8124
8170
  value
8125
8171
  });
@@ -8134,6 +8180,27 @@ async function previewReplacements(dir, config) {
8134
8180
  // src/cli/commands/init.ts
8135
8181
  init_fs();
8136
8182
 
8183
+ // src/lib/utils/paths.ts
8184
+ init_cjs_shims();
8185
+ var import_node_fs = __toESM(require("fs"), 1);
8186
+ var import_node_path3 = __toESM(require("path"), 1);
8187
+ var import_node_url = require("url");
8188
+ function getPackageRoot() {
8189
+ const currentFilePath = (0, import_node_url.fileURLToPath)(importMetaUrl);
8190
+ let currentDir = import_node_path3.default.dirname(currentFilePath);
8191
+ while (currentDir !== import_node_path3.default.dirname(currentDir)) {
8192
+ const packageJsonPath = import_node_path3.default.join(currentDir, "package.json");
8193
+ if (import_node_fs.default.existsSync(packageJsonPath)) {
8194
+ return currentDir;
8195
+ }
8196
+ currentDir = import_node_path3.default.dirname(currentDir);
8197
+ }
8198
+ throw new Error("Could not find package root (no package.json found in parent directories)");
8199
+ }
8200
+ function getTemplatesPath() {
8201
+ return import_node_path3.default.join(getPackageRoot(), "templates");
8202
+ }
8203
+
8137
8204
  // src/lib/utils/prompt-cancel.ts
8138
8205
  init_cjs_shims();
8139
8206
  var readline = __toESM(require("readline"), 1);
@@ -10664,6 +10731,61 @@ async function promptMcpConfig(options) {
10664
10731
  const installedServers = await getInstalledMcpServers(projectPath);
10665
10732
  const userInstalledSet = new Set(installedServers.user);
10666
10733
  const projectInstalledSet = new Set(installedServers.project);
10734
+ const availableServers = MCP_SERVERS.filter(
10735
+ (s) => !userInstalledSet.has(s.id) && !projectInstalledSet.has(s.id)
10736
+ );
10737
+ if (availableServers.length === 0) {
10738
+ logger.newline();
10739
+ logger.success("All MCP servers are already installed!");
10740
+ const parts = [];
10741
+ if (userInstalledSet.size > 0) {
10742
+ parts.push(`${userInstalledSet.size} at user level`);
10743
+ }
10744
+ if (projectInstalledSet.size > 0) {
10745
+ parts.push(`${projectInstalledSet.size} at project level`);
10746
+ }
10747
+ logger.info(colors.muted(` (${parts.join(", ")})`));
10748
+ logger.newline();
10749
+ const wantCustom2 = await confirm({
10750
+ message: "Do you want to add a custom MCP server?",
10751
+ default: false
10752
+ });
10753
+ if (wantCustom2) {
10754
+ const level2 = await select({
10755
+ message: "Where should the custom server be configured?",
10756
+ choices: [
10757
+ {
10758
+ name: "Project level (.claude/settings.local.json)",
10759
+ value: "project",
10760
+ description: "Specific to this project"
10761
+ },
10762
+ {
10763
+ name: "User level (~/.claude/settings.json)",
10764
+ value: "user",
10765
+ description: "Available in all projects"
10766
+ }
10767
+ ],
10768
+ default: options?.defaults?.level || "project"
10769
+ });
10770
+ const customInstallation = await promptCustomServer(level2);
10771
+ if (customInstallation) {
10772
+ return {
10773
+ config: {
10774
+ level: level2,
10775
+ servers: [customInstallation]
10776
+ },
10777
+ skippedConfigs: []
10778
+ };
10779
+ }
10780
+ }
10781
+ return {
10782
+ config: {
10783
+ level: "project",
10784
+ servers: []
10785
+ },
10786
+ skippedConfigs: []
10787
+ };
10788
+ }
10667
10789
  const level = await select({
10668
10790
  message: "Where should MCP servers be configured?",
10669
10791
  choices: [
@@ -10697,37 +10819,18 @@ async function promptMcpConfig(options) {
10697
10819
  }
10698
10820
  logger.newline();
10699
10821
  for (const [category, servers] of Object.entries(serversByCategory)) {
10700
- const choices = servers.map((s) => {
10701
- const isInstalledAtUserLevel = userInstalledSet.has(s.id);
10702
- const isInstalledAtProjectLevel = projectInstalledSet.has(s.id);
10703
- if (isInstalledAtUserLevel) {
10704
- return {
10705
- name: `${s.name} - ${s.description} ${colors.muted("(already installed at user level)")}`,
10706
- value: s.id,
10707
- checked: false,
10708
- disabled: "already installed at user level"
10709
- };
10710
- }
10711
- if (isInstalledAtProjectLevel) {
10712
- return {
10713
- name: `${s.name} - ${s.description} ${colors.muted("(already installed at project level)")}`,
10714
- value: s.id,
10715
- checked: false,
10716
- disabled: "already installed at project level"
10717
- };
10718
- }
10719
- return {
10720
- name: `${s.name} - ${s.description}`,
10721
- value: s.id,
10722
- checked: options?.defaults?.servers?.some((i) => i.serverId === s.id) ?? false
10723
- };
10724
- });
10725
- const hasEnabledChoices = choices.some((choice) => !choice.disabled);
10726
- const categoryLabel = formatCategory(category);
10727
- if (!hasEnabledChoices) {
10728
- logger.info(colors.muted(`${categoryLabel}: All servers already installed`));
10822
+ const availableInCategory = servers.filter(
10823
+ (s) => !userInstalledSet.has(s.id) && !projectInstalledSet.has(s.id)
10824
+ );
10825
+ if (availableInCategory.length === 0) {
10729
10826
  continue;
10730
10827
  }
10828
+ const choices = availableInCategory.map((s) => ({
10829
+ name: `${s.name} - ${s.description}`,
10830
+ value: s.id,
10831
+ checked: options?.defaults?.servers?.some((i) => i.serverId === s.id) ?? false
10832
+ }));
10833
+ const categoryLabel = formatCategory(category);
10731
10834
  const selected = await checkbox({
10732
10835
  message: `${categoryLabel}:`,
10733
10836
  choices