@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.js CHANGED
@@ -5500,12 +5500,13 @@ var DEFAULT_DENY_RULES = [
5500
5500
  "Write(/sbin/**)",
5501
5501
  "Write(/var/**)",
5502
5502
  "Write(/tmp/**)",
5503
- // Dangerous commands
5503
+ // Dangerous commands - use exact matches or :* prefix
5504
5504
  "Bash(rm -rf /)",
5505
- "Bash(sudo *)",
5506
- "Bash(chmod 777 *)",
5507
- "Bash(curl * | bash)",
5508
- "Bash(wget * | bash)",
5505
+ "Bash(sudo:*)",
5506
+ "Bash(chmod 777:*)",
5507
+ // Note: Can't deny "curl * | bash" pattern - use specific denies instead
5508
+ "Bash(curl:*)",
5509
+ "Bash(wget:*)",
5509
5510
  // Sensitive files
5510
5511
  "Write(.env)",
5511
5512
  "Write(.env.*)",
@@ -5571,64 +5572,69 @@ function generateAllowRules(config) {
5571
5572
  }
5572
5573
  if (config.git.readOnly) {
5573
5574
  rules.push(
5574
- "Bash(git status*)",
5575
- "Bash(git diff*)",
5576
- "Bash(git log*)",
5577
- "Bash(git show*)",
5578
- "Bash(git branch*)"
5575
+ "Bash(git status:*)",
5576
+ "Bash(git diff:*)",
5577
+ "Bash(git log:*)",
5578
+ "Bash(git show:*)",
5579
+ "Bash(git branch:*)"
5579
5580
  );
5580
5581
  }
5581
5582
  if (config.git.staging) {
5582
- rules.push("Bash(git add*)");
5583
+ rules.push("Bash(git add:*)");
5583
5584
  }
5584
5585
  if (config.git.commit) {
5585
- rules.push("Bash(git commit*)");
5586
+ rules.push("Bash(git commit:*)");
5586
5587
  }
5587
5588
  if (config.git.push) {
5588
- rules.push("Bash(git push*)");
5589
+ rules.push("Bash(git push:*)");
5589
5590
  }
5590
5591
  if (config.git.branching) {
5591
- rules.push("Bash(git checkout*)", "Bash(git branch*)", "Bash(git merge*)", "Bash(git rebase*)");
5592
+ rules.push(
5593
+ "Bash(git checkout:*)",
5594
+ "Bash(git branch:*)",
5595
+ "Bash(git merge:*)",
5596
+ "Bash(git rebase:*)"
5597
+ );
5592
5598
  }
5593
5599
  if (config.bash.packageManager) {
5594
5600
  rules.push(
5595
- "Bash(pnpm *)",
5596
- "Bash(npm *)",
5597
- "Bash(yarn *)",
5598
- "Bash(bun *)",
5599
- "Bash(npx *)",
5600
- "Bash(bunx *)"
5601
+ "Bash(pnpm:*)",
5602
+ "Bash(npm:*)",
5603
+ "Bash(yarn:*)",
5604
+ "Bash(bun:*)",
5605
+ "Bash(npx:*)",
5606
+ "Bash(bunx:*)"
5601
5607
  );
5602
5608
  }
5603
5609
  if (config.bash.testing) {
5604
5610
  rules.push(
5605
- "Bash(vitest*)",
5606
- "Bash(jest*)",
5607
- "Bash(playwright*)",
5608
- "Bash(cypress*)",
5609
- "Bash(pnpm test*)",
5610
- "Bash(npm test*)",
5611
- "Bash(pnpm run test*)",
5612
- "Bash(npm run test*)"
5611
+ "Bash(vitest:*)",
5612
+ "Bash(jest:*)",
5613
+ "Bash(playwright:*)",
5614
+ "Bash(cypress:*)",
5615
+ "Bash(pnpm test:*)",
5616
+ "Bash(npm test:*)",
5617
+ "Bash(pnpm run test:*)",
5618
+ "Bash(npm run test:*)"
5613
5619
  );
5614
5620
  }
5615
5621
  if (config.bash.building) {
5616
5622
  rules.push(
5617
- "Bash(pnpm build*)",
5618
- "Bash(npm run build*)",
5619
- "Bash(pnpm run build*)",
5620
- "Bash(tsc*)",
5621
- "Bash(tsup*)",
5622
- "Bash(vite build*)",
5623
- "Bash(next build*)",
5624
- "Bash(astro build*)"
5623
+ "Bash(pnpm build:*)",
5624
+ "Bash(npm run build:*)",
5625
+ "Bash(pnpm run build:*)",
5626
+ "Bash(tsc:*)",
5627
+ "Bash(tsup:*)",
5628
+ "Bash(vite build:*)",
5629
+ "Bash(next build:*)",
5630
+ "Bash(astro build:*)"
5625
5631
  );
5626
5632
  }
5627
5633
  if (config.bash.docker) {
5628
- rules.push("Bash(docker *)", "Bash(docker-compose *)");
5634
+ rules.push("Bash(docker:*)", "Bash(docker-compose:*)");
5629
5635
  }
5630
5636
  if (config.bash.arbitrary) {
5631
- rules.push("Bash(*)");
5637
+ rules.push("Bash");
5632
5638
  }
5633
5639
  if (config.web.fetch) {
5634
5640
  rules.push("WebFetch");
@@ -6042,29 +6048,6 @@ function showReplacementReport(report) {
6042
6048
  // src/lib/scaffold/claude-md-generator.ts
6043
6049
  init_esm_shims();
6044
6050
  init_fs();
6045
-
6046
- // src/lib/utils/paths.ts
6047
- init_esm_shims();
6048
- import fs3 from "fs";
6049
- import path5 from "path";
6050
- import { fileURLToPath as fileURLToPath2 } from "url";
6051
- function getPackageRoot() {
6052
- const currentFilePath = fileURLToPath2(import.meta.url);
6053
- let currentDir = path5.dirname(currentFilePath);
6054
- while (currentDir !== path5.dirname(currentDir)) {
6055
- const packageJsonPath = path5.join(currentDir, "package.json");
6056
- if (fs3.existsSync(packageJsonPath)) {
6057
- return currentDir;
6058
- }
6059
- currentDir = path5.dirname(currentDir);
6060
- }
6061
- throw new Error("Could not find package root (no package.json found in parent directories)");
6062
- }
6063
- function getTemplatesPath() {
6064
- return path5.join(getPackageRoot(), "templates");
6065
- }
6066
-
6067
- // src/lib/scaffold/claude-md-generator.ts
6068
6051
  async function generateClaudeMd(projectPath, projectInfo, options) {
6069
6052
  const claudeMdPath = joinPath(projectPath, "CLAUDE.md");
6070
6053
  const exists = await pathExists(claudeMdPath);
@@ -6076,18 +6059,12 @@ async function generateClaudeMd(projectPath, projectInfo, options) {
6076
6059
  };
6077
6060
  }
6078
6061
  try {
6079
- let template;
6062
+ let content;
6080
6063
  if (options?.customTemplate) {
6081
- template = options.customTemplate;
6064
+ content = processCustomTemplate(options.customTemplate, projectInfo);
6082
6065
  } else {
6083
- const templatePath = joinPath(getTemplatesPath(), "CLAUDE.md.template");
6084
- if (await pathExists(templatePath)) {
6085
- template = await readFile(templatePath);
6086
- } else {
6087
- template = getMinimalTemplate();
6088
- }
6066
+ content = generateClaudeMdContent(projectInfo, options);
6089
6067
  }
6090
- const content = processTemplate(template, projectInfo, options);
6091
6068
  await writeFile(claudeMdPath, content);
6092
6069
  return {
6093
6070
  created: true,
@@ -6112,41 +6089,165 @@ async function generateClaudeMdWithSpinner(projectPath, projectInfo, options) {
6112
6089
  }
6113
6090
  );
6114
6091
  }
6115
- function processTemplate(template, projectInfo, options) {
6116
- let content = template;
6092
+ function processCustomTemplate(template, projectInfo) {
6093
+ 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 || "");
6094
+ }
6095
+ function generateClaudeMdContent(projectInfo, options) {
6117
6096
  const techStack = options?.templateConfig?.techStack;
6118
6097
  const commands = options?.templateConfig?.commands;
6119
6098
  const targets = options?.templateConfig?.targets;
6120
6099
  const preferences = options?.claudeConfig?.preferences;
6121
6100
  const standards = options?.claudeConfig?.extras?.standards;
6122
- 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 || "");
6123
6101
  const packageManager = preferences?.packageManager || "pnpm";
6124
- content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
6125
- const coverageTarget = standards?.testing?.coverageTarget ? String(standards.testing.coverageTarget) : targets && "coverage" in targets ? String(targets.coverage) : "90";
6126
- content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
6127
- content = processStandardsPlaceholders(content, standards, preferences);
6102
+ const lines = [];
6103
+ lines.push("# CLAUDE.md");
6104
+ lines.push("");
6105
+ lines.push("## Project Overview");
6106
+ lines.push("");
6107
+ lines.push(`**${projectInfo.name}** - ${projectInfo.description}`);
6108
+ lines.push("");
6109
+ lines.push("## Repository");
6110
+ lines.push("");
6111
+ lines.push(`- **Organization:** ${projectInfo.org}`);
6112
+ lines.push(`- **Repository:** ${projectInfo.repo}`);
6113
+ lines.push(`- **GitHub:** https://github.com/${projectInfo.org}/${projectInfo.repo}`);
6128
6114
  if (projectInfo.domain) {
6129
- content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
6130
- } else {
6131
- content = content.replace(/\{\{#if DOMAIN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6115
+ lines.push(`- **Domain:** ${projectInfo.domain}`);
6132
6116
  }
6133
- if (techStack && Object.keys(techStack).length > 0) {
6134
- const techStackContent = generateTechStackSection(techStack);
6135
- content = content.replace(/\{\{#if TECH_STACK\}\}/g, "").replace(/\{\{TECH_STACK\}\}\n\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, techStackContent);
6136
- } else {
6137
- content = content.replace(/\{\{#if TECH_STACK\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6117
+ lines.push("");
6118
+ lines.push("## Tech Stack");
6119
+ lines.push("");
6120
+ lines.push(generateTechStackSection(techStack));
6121
+ lines.push("## Project Structure");
6122
+ lines.push("");
6123
+ lines.push("```text");
6124
+ lines.push(`${projectInfo.name}/`);
6125
+ lines.push("\u251C\u2500\u2500 src/ # Source code");
6126
+ lines.push("\u251C\u2500\u2500 tests/ # Test files");
6127
+ lines.push("\u251C\u2500\u2500 docs/ # Documentation");
6128
+ lines.push("\u2514\u2500\u2500 .claude/ # Claude configuration");
6129
+ lines.push(" \u251C\u2500\u2500 agents/ # AI agent definitions");
6130
+ lines.push(" \u251C\u2500\u2500 commands/ # Custom slash commands");
6131
+ lines.push(" \u251C\u2500\u2500 skills/ # Specialized skills");
6132
+ lines.push(" \u2514\u2500\u2500 docs/ # AI-specific documentation");
6133
+ lines.push("```");
6134
+ lines.push("");
6135
+ lines.push("## Quick Commands");
6136
+ lines.push("");
6137
+ lines.push(generateCommandsSection(commands, packageManager));
6138
+ lines.push("## Development Guidelines");
6139
+ lines.push("");
6140
+ lines.push("### Code Standards");
6141
+ lines.push("");
6142
+ lines.push("- Primary language: TypeScript");
6143
+ lines.push("- Follow TypeScript best practices");
6144
+ if (standards?.code?.namedExportsOnly) {
6145
+ lines.push("- Use named exports only (no default exports)");
6138
6146
  }
6139
- if (commands && Object.keys(commands).length > 0) {
6140
- const commandsContent = generateCommandsSection(commands, packageManager);
6141
- content = content.replace(/\{\{#if COMMANDS\}\}/g, "").replace(/\{\{COMMANDS\}\}\n\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, commandsContent);
6147
+ const maxLines = standards?.code?.maxFileLines || 500;
6148
+ lines.push(`- Maximum ${maxLines} lines per file`);
6149
+ if (standards?.code?.jsDocRequired) {
6150
+ lines.push("- Document all exports with JSDoc");
6151
+ }
6152
+ if (standards?.code?.roroPattern) {
6153
+ lines.push("- Use RO-RO pattern (Receive Object, Return Object)");
6154
+ }
6155
+ lines.push("");
6156
+ lines.push("### Testing");
6157
+ lines.push("");
6158
+ if (standards?.testing?.tddRequired) {
6159
+ lines.push("- Methodology: TDD (Test-Driven Development)");
6160
+ lines.push("- Write tests first: Red -> Green -> Refactor");
6142
6161
  } else {
6143
- content = content.replace(/\{\{#if COMMANDS\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6162
+ lines.push("- Write comprehensive tests for all features");
6163
+ }
6164
+ const coverageTarget = standards?.testing?.coverageTarget || (targets && "coverage" in targets ? targets.coverage : 90);
6165
+ lines.push(`- Maintain ${coverageTarget}%+ code coverage`);
6166
+ const testPattern = standards?.testing?.testPattern === "gwt" ? "GWT (Given-When-Then)" : "AAA (Arrange, Act, Assert)";
6167
+ lines.push(`- Test pattern: ${testPattern}`);
6168
+ if (standards?.testing?.testLocation) {
6169
+ const testLocationText = standards.testing.testLocation === "colocated" ? "Co-located with source" : "Separate test directory";
6170
+ lines.push(`- Test location: ${testLocationText}`);
6171
+ }
6172
+ lines.push("");
6173
+ lines.push("### Git Workflow");
6174
+ lines.push("");
6175
+ lines.push("- Use conventional commits: `type(scope): description`");
6176
+ lines.push("- Types: feat, fix, docs, style, refactor, test, chore");
6177
+ lines.push("- Keep commits atomic and focused");
6178
+ if (preferences?.includeCoAuthor) {
6179
+ lines.push("");
6180
+ lines.push("#### Commit Attribution");
6181
+ lines.push("");
6182
+ lines.push("Include the following in commit messages:");
6183
+ lines.push("```");
6184
+ lines.push("\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)");
6185
+ lines.push("");
6186
+ lines.push("Co-Authored-By: Claude <noreply@anthropic.com>");
6187
+ lines.push("```");
6188
+ }
6189
+ lines.push("");
6190
+ lines.push("## Claude Behavior Guidelines");
6191
+ lines.push("");
6192
+ lines.push("### Critical Thinking");
6193
+ lines.push("");
6194
+ lines.push("- You are an expert who double-checks things, you are skeptical and do research");
6195
+ lines.push("- Neither the user nor you are always right, but both strive for accuracy");
6196
+ lines.push("- When the user asks something, reason with these questions:");
6197
+ lines.push(' - "Why might the user be wrong?"');
6198
+ lines.push(' - "What arguments exist against what the user thinks?"');
6199
+ lines.push(` - "Act as devil's advocate - why might this proposal fail?"`);
6200
+ lines.push(` - "Imagine you're in a debate - how would you refute this?"`);
6201
+ lines.push(
6202
+ "- Always ask to better understand the context of the requested change before implementing"
6203
+ );
6204
+ lines.push("");
6205
+ lines.push("### Communication Style");
6206
+ lines.push("");
6207
+ if (preferences?.responseLanguage) {
6208
+ const langDisplay = preferences.responseLanguage === "es" ? "Spanish" : preferences.responseLanguage === "en" ? "English" : preferences.responseLanguage;
6209
+ lines.push(`- Respond in ${langDisplay}`);
6144
6210
  }
6145
- content = content.replace(/\{\{#if PROJECT_STRUCTURE\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6146
- return content;
6211
+ lines.push("- Code and comments should always be in English");
6212
+ lines.push("- Be direct and concise");
6213
+ lines.push('- Explain the "why" behind decisions when relevant');
6214
+ lines.push("");
6215
+ lines.push("### Writing Style");
6216
+ lines.push("");
6217
+ lines.push(
6218
+ '- Systematically replace em-dashes ("\u2014") with a period (".") to start a new sentence, or a comma (",") to continue the sentence'
6219
+ );
6220
+ lines.push("- Avoid unnecessary filler words");
6221
+ lines.push("- Use clear, technical language");
6222
+ lines.push("");
6223
+ lines.push("## Claude Configuration");
6224
+ lines.push("");
6225
+ lines.push("This project uses `@qazuor/claude-code-config` for AI-assisted development.");
6226
+ lines.push("");
6227
+ lines.push("### Available Commands");
6228
+ lines.push("");
6229
+ lines.push("Run `/help` in Claude to see all available commands.");
6230
+ lines.push("");
6231
+ lines.push("### Documentation");
6232
+ lines.push("");
6233
+ lines.push("- Quick Start: `.claude/docs/quick-start.md`");
6234
+ lines.push("- Workflows: `.claude/docs/workflows/README.md`");
6235
+ lines.push("- Standards: `.claude/docs/standards/`");
6236
+ lines.push("");
6237
+ lines.push("---");
6238
+ lines.push("");
6239
+ lines.push(
6240
+ "*Generated by [@qazuor/claude-code-config](https://github.com/qazuor/claude-code-config)*"
6241
+ );
6242
+ lines.push("");
6243
+ return lines.join("\n");
6147
6244
  }
6148
6245
  function generateTechStackSection(techStack) {
6246
+ if (!techStack) {
6247
+ return getDefaultTechStack();
6248
+ }
6149
6249
  const lines = [];
6250
+ let hasContent = false;
6150
6251
  if (techStack.frontendFramework && techStack.frontendFramework !== "None") {
6151
6252
  lines.push("**Frontend:**");
6152
6253
  lines.push(`- Framework: ${techStack.frontendFramework}`);
@@ -6154,6 +6255,7 @@ function generateTechStackSection(techStack) {
6154
6255
  lines.push(`- State: ${techStack.stateManagement}`);
6155
6256
  }
6156
6257
  lines.push("");
6258
+ hasContent = true;
6157
6259
  }
6158
6260
  if (techStack.apiFramework && techStack.apiFramework !== "None") {
6159
6261
  lines.push("**Backend:**");
@@ -6162,145 +6264,89 @@ function generateTechStackSection(techStack) {
6162
6264
  lines.push(`- Validation: ${techStack.validationLibrary}`);
6163
6265
  }
6164
6266
  lines.push("");
6267
+ hasContent = true;
6165
6268
  }
6166
6269
  if (techStack.databaseOrm && techStack.databaseOrm !== "None") {
6167
6270
  lines.push("**Database:**");
6168
6271
  lines.push(`- ORM: ${techStack.databaseOrm}`);
6169
6272
  lines.push("");
6273
+ hasContent = true;
6170
6274
  }
6171
6275
  if (techStack.authPattern && techStack.authPattern !== "None") {
6172
6276
  lines.push("**Authentication:**");
6173
6277
  lines.push(`- Provider: ${techStack.authPattern}`);
6174
6278
  lines.push("");
6279
+ hasContent = true;
6175
6280
  }
6176
6281
  if (techStack.testFramework && techStack.testFramework !== "None") {
6177
6282
  lines.push("**Testing:**");
6178
6283
  lines.push(`- Framework: ${techStack.testFramework}`);
6179
6284
  lines.push("");
6285
+ hasContent = true;
6180
6286
  }
6181
6287
  if (techStack.bundler && techStack.bundler !== "None") {
6182
6288
  lines.push("**Build:**");
6183
6289
  lines.push(`- Bundler: ${techStack.bundler}`);
6184
6290
  lines.push("");
6291
+ hasContent = true;
6292
+ }
6293
+ if (!hasContent) {
6294
+ return getDefaultTechStack();
6185
6295
  }
6186
6296
  return lines.join("\n");
6187
6297
  }
6188
- function generateCommandsSection(commands, packageManager) {
6298
+ function getDefaultTechStack() {
6299
+ return `**Frontend:**
6300
+ - Framework: Not configured
6301
+
6302
+ **Backend:**
6303
+ - API: Not configured
6304
+
6305
+ **Database:**
6306
+ - ORM: Not configured
6307
+
6308
+ **Testing:**
6309
+ - Framework: Not configured
6310
+
6311
+ `;
6312
+ }
6313
+ function generateCommandsSection(commands, packageManager = "pnpm") {
6189
6314
  const lines = ["```bash"];
6190
6315
  lines.push("# Development");
6191
6316
  lines.push(`${packageManager} dev # Start development server`);
6192
6317
  lines.push("");
6193
6318
  lines.push("# Testing");
6194
- if (commands.test) {
6319
+ if (commands?.test) {
6195
6320
  lines.push(`${commands.test} # Run tests`);
6196
6321
  } else {
6197
6322
  lines.push(`${packageManager} test # Run tests`);
6198
6323
  }
6199
- if (commands.coverage) {
6324
+ if (commands?.coverage) {
6200
6325
  lines.push(`${commands.coverage} # Run tests with coverage`);
6201
6326
  } else {
6202
6327
  lines.push(`${packageManager} test:coverage # Run tests with coverage`);
6203
6328
  }
6204
6329
  lines.push("");
6205
6330
  lines.push("# Quality");
6206
- if (commands.lint) {
6331
+ if (commands?.lint) {
6207
6332
  lines.push(`${commands.lint} # Run linter`);
6208
6333
  } else {
6209
6334
  lines.push(`${packageManager} lint # Run linter`);
6210
6335
  }
6211
- if (commands.typecheck) {
6336
+ if (commands?.typecheck) {
6212
6337
  lines.push(`${commands.typecheck} # Type checking`);
6213
6338
  } else {
6214
6339
  lines.push(`${packageManager} typecheck # Type checking`);
6215
6340
  }
6216
- if (commands.build) {
6341
+ if (commands?.build) {
6217
6342
  lines.push("");
6218
6343
  lines.push("# Build");
6219
6344
  lines.push(`${commands.build} # Build for production`);
6220
6345
  }
6221
6346
  lines.push("```");
6347
+ lines.push("");
6222
6348
  return lines.join("\n");
6223
6349
  }
6224
- function processStandardsPlaceholders(content, standards, preferences) {
6225
- let result = content;
6226
- const primaryLanguage = "TypeScript";
6227
- result = result.replace(/\{\{PRIMARY_LANGUAGE\}\}/g, primaryLanguage);
6228
- const maxFileLines = standards?.code?.maxFileLines?.toString() || "500";
6229
- result = result.replace(/\{\{MAX_FILE_LINES\}\}/g, maxFileLines);
6230
- const testPattern = standards?.testing?.testPattern === "gwt" ? "GWT (Given-When-Then)" : "AAA (Arrange, Act, Assert)";
6231
- result = result.replace(/\{\{TEST_PATTERN\}\}/g, testPattern);
6232
- const responseLanguage = preferences?.responseLanguage === "es" ? "Spanish" : preferences?.responseLanguage === "en" ? "English" : "Spanish";
6233
- result = result.replace(/\{\{RESPONSE_LANGUAGE\}\}/g, responseLanguage);
6234
- if (standards?.code?.namedExportsOnly) {
6235
- result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6236
- } else {
6237
- result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6238
- }
6239
- if (standards?.code?.jsDocRequired) {
6240
- result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6241
- } else {
6242
- result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6243
- }
6244
- if (standards?.code?.roroPattern) {
6245
- result = result.replace(/\{\{#if RORO_PATTERN\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6246
- } else {
6247
- result = result.replace(/\{\{#if RORO_PATTERN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6248
- }
6249
- if (standards?.testing?.tddRequired) {
6250
- result = result.replace(/\{\{#if TDD_REQUIRED\}\}/g, "").replace(/\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6251
- } else {
6252
- result = result.replace(/\{\{#if TDD_REQUIRED\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6253
- }
6254
- const testLocation = standards?.testing?.testLocation;
6255
- if (testLocation) {
6256
- const testLocationText = testLocation === "colocated" ? "Co-located with source" : "Separate test directory";
6257
- result = result.replace(/\{\{#if TEST_LOCATION\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{TEST_LOCATION\}\}/g, testLocationText);
6258
- } else {
6259
- result = result.replace(/\{\{#if TEST_LOCATION\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6260
- }
6261
- if (preferences?.includeCoAuthor) {
6262
- result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6263
- } else {
6264
- result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6265
- }
6266
- if (preferences?.responseLanguage) {
6267
- result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6268
- } else {
6269
- result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6270
- }
6271
- return result;
6272
- }
6273
- function getMinimalTemplate() {
6274
- return `# CLAUDE.md
6275
-
6276
- ## Project Overview
6277
-
6278
- **{{PROJECT_NAME}}** - {{PROJECT_DESCRIPTION}}
6279
-
6280
- ## Repository
6281
-
6282
- - **GitHub:** https://github.com/{{ORG}}/{{REPO}}
6283
-
6284
- ## Quick Commands
6285
-
6286
- \`\`\`bash
6287
- {{PACKAGE_MANAGER}} dev # Start development
6288
- {{PACKAGE_MANAGER}} test # Run tests
6289
- {{PACKAGE_MANAGER}} lint # Run linter
6290
- {{PACKAGE_MANAGER}} build # Build project
6291
- \`\`\`
6292
-
6293
- ## Claude Configuration
6294
-
6295
- This project uses \`@qazuor/claude-code-config\` for AI-assisted development.
6296
-
6297
- See \`.claude/docs/quick-start.md\` for getting started.
6298
-
6299
- ---
6300
-
6301
- *Generated by [@qazuor/claude-code-config](https://github.com/qazuor/claude-code-config)*
6302
- `;
6303
- }
6304
6350
 
6305
6351
  // src/lib/scaffold/index.ts
6306
6352
  init_esm_shims();
@@ -7053,8 +7099,8 @@ function buildSettingsLocalJson(options) {
7053
7099
 
7054
7100
  // src/lib/templates/config-replacer.ts
7055
7101
  init_esm_shims();
7056
- import * as fs4 from "fs/promises";
7057
- import * as path6 from "path";
7102
+ import * as fs3 from "fs/promises";
7103
+ import * as path5 from "path";
7058
7104
  import ora2 from "ora";
7059
7105
 
7060
7106
  // src/constants/template-placeholders.ts
@@ -7962,7 +8008,7 @@ function flattenTemplateConfig(config) {
7962
8008
  return flattened;
7963
8009
  }
7964
8010
  function shouldProcessFile(filePath) {
7965
- const ext = path6.extname(filePath).toLowerCase();
8011
+ const ext = path5.extname(filePath).toLowerCase();
7966
8012
  return PROCESSABLE_EXTENSIONS.includes(ext);
7967
8013
  }
7968
8014
  function shouldSkipDirectory(dirName) {
@@ -7971,9 +8017,9 @@ function shouldSkipDirectory(dirName) {
7971
8017
  async function getAllFiles(dir) {
7972
8018
  const files = [];
7973
8019
  try {
7974
- const entries = await fs4.readdir(dir, { withFileTypes: true });
8020
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
7975
8021
  for (const entry of entries) {
7976
- const fullPath = path6.join(dir, entry.name);
8022
+ const fullPath = path5.join(dir, entry.name);
7977
8023
  if (entry.isDirectory()) {
7978
8024
  if (!shouldSkipDirectory(entry.name)) {
7979
8025
  const subFiles = await getAllFiles(fullPath);
@@ -7990,7 +8036,7 @@ async function getAllFiles(dir) {
7990
8036
  async function replaceInFile2(filePath, replacements) {
7991
8037
  const changes = [];
7992
8038
  try {
7993
- let content = await fs4.readFile(filePath, "utf-8");
8039
+ let content = await fs3.readFile(filePath, "utf-8");
7994
8040
  let modified = false;
7995
8041
  for (const [placeholder, value] of Object.entries(replacements)) {
7996
8042
  if (content.includes(placeholder)) {
@@ -8000,7 +8046,7 @@ async function replaceInFile2(filePath, replacements) {
8000
8046
  }
8001
8047
  }
8002
8048
  if (modified) {
8003
- await fs4.writeFile(filePath, content, "utf-8");
8049
+ await fs3.writeFile(filePath, content, "utf-8");
8004
8050
  }
8005
8051
  } catch {
8006
8052
  }
@@ -8021,7 +8067,7 @@ async function replaceTemplatePlaceholders(dir, config) {
8021
8067
  report.filesModified++;
8022
8068
  for (const change of changes) {
8023
8069
  report.replacements.push({
8024
- file: path6.relative(dir, file),
8070
+ file: path5.relative(dir, file),
8025
8071
  placeholder: change.placeholder,
8026
8072
  value: change.value
8027
8073
  });
@@ -8092,11 +8138,11 @@ async function previewReplacements(dir, config) {
8092
8138
  const preview = [];
8093
8139
  for (const file of files) {
8094
8140
  try {
8095
- const content = await fs4.readFile(file, "utf-8");
8141
+ const content = await fs3.readFile(file, "utf-8");
8096
8142
  for (const [placeholder, value] of Object.entries(replacements)) {
8097
8143
  if (content.includes(placeholder)) {
8098
8144
  preview.push({
8099
- file: path6.relative(dir, file),
8145
+ file: path5.relative(dir, file),
8100
8146
  placeholder,
8101
8147
  value
8102
8148
  });
@@ -8111,6 +8157,27 @@ async function previewReplacements(dir, config) {
8111
8157
  // src/cli/commands/init.ts
8112
8158
  init_fs();
8113
8159
 
8160
+ // src/lib/utils/paths.ts
8161
+ init_esm_shims();
8162
+ import fs4 from "fs";
8163
+ import path6 from "path";
8164
+ import { fileURLToPath as fileURLToPath2 } from "url";
8165
+ function getPackageRoot() {
8166
+ const currentFilePath = fileURLToPath2(import.meta.url);
8167
+ let currentDir = path6.dirname(currentFilePath);
8168
+ while (currentDir !== path6.dirname(currentDir)) {
8169
+ const packageJsonPath = path6.join(currentDir, "package.json");
8170
+ if (fs4.existsSync(packageJsonPath)) {
8171
+ return currentDir;
8172
+ }
8173
+ currentDir = path6.dirname(currentDir);
8174
+ }
8175
+ throw new Error("Could not find package root (no package.json found in parent directories)");
8176
+ }
8177
+ function getTemplatesPath() {
8178
+ return path6.join(getPackageRoot(), "templates");
8179
+ }
8180
+
8114
8181
  // src/lib/utils/prompt-cancel.ts
8115
8182
  init_esm_shims();
8116
8183
  import * as readline from "readline";
@@ -10647,6 +10714,61 @@ async function promptMcpConfig(options) {
10647
10714
  const installedServers = await getInstalledMcpServers(projectPath);
10648
10715
  const userInstalledSet = new Set(installedServers.user);
10649
10716
  const projectInstalledSet = new Set(installedServers.project);
10717
+ const availableServers = MCP_SERVERS.filter(
10718
+ (s) => !userInstalledSet.has(s.id) && !projectInstalledSet.has(s.id)
10719
+ );
10720
+ if (availableServers.length === 0) {
10721
+ logger.newline();
10722
+ logger.success("All MCP servers are already installed!");
10723
+ const parts = [];
10724
+ if (userInstalledSet.size > 0) {
10725
+ parts.push(`${userInstalledSet.size} at user level`);
10726
+ }
10727
+ if (projectInstalledSet.size > 0) {
10728
+ parts.push(`${projectInstalledSet.size} at project level`);
10729
+ }
10730
+ logger.info(colors.muted(` (${parts.join(", ")})`));
10731
+ logger.newline();
10732
+ const wantCustom2 = await confirm({
10733
+ message: "Do you want to add a custom MCP server?",
10734
+ default: false
10735
+ });
10736
+ if (wantCustom2) {
10737
+ const level2 = await select({
10738
+ message: "Where should the custom server be configured?",
10739
+ choices: [
10740
+ {
10741
+ name: "Project level (.claude/settings.local.json)",
10742
+ value: "project",
10743
+ description: "Specific to this project"
10744
+ },
10745
+ {
10746
+ name: "User level (~/.claude/settings.json)",
10747
+ value: "user",
10748
+ description: "Available in all projects"
10749
+ }
10750
+ ],
10751
+ default: options?.defaults?.level || "project"
10752
+ });
10753
+ const customInstallation = await promptCustomServer(level2);
10754
+ if (customInstallation) {
10755
+ return {
10756
+ config: {
10757
+ level: level2,
10758
+ servers: [customInstallation]
10759
+ },
10760
+ skippedConfigs: []
10761
+ };
10762
+ }
10763
+ }
10764
+ return {
10765
+ config: {
10766
+ level: "project",
10767
+ servers: []
10768
+ },
10769
+ skippedConfigs: []
10770
+ };
10771
+ }
10650
10772
  const level = await select({
10651
10773
  message: "Where should MCP servers be configured?",
10652
10774
  choices: [
@@ -10680,37 +10802,18 @@ async function promptMcpConfig(options) {
10680
10802
  }
10681
10803
  logger.newline();
10682
10804
  for (const [category, servers] of Object.entries(serversByCategory)) {
10683
- const choices = servers.map((s) => {
10684
- const isInstalledAtUserLevel = userInstalledSet.has(s.id);
10685
- const isInstalledAtProjectLevel = projectInstalledSet.has(s.id);
10686
- if (isInstalledAtUserLevel) {
10687
- return {
10688
- name: `${s.name} - ${s.description} ${colors.muted("(already installed at user level)")}`,
10689
- value: s.id,
10690
- checked: false,
10691
- disabled: "already installed at user level"
10692
- };
10693
- }
10694
- if (isInstalledAtProjectLevel) {
10695
- return {
10696
- name: `${s.name} - ${s.description} ${colors.muted("(already installed at project level)")}`,
10697
- value: s.id,
10698
- checked: false,
10699
- disabled: "already installed at project level"
10700
- };
10701
- }
10702
- return {
10703
- name: `${s.name} - ${s.description}`,
10704
- value: s.id,
10705
- checked: options?.defaults?.servers?.some((i) => i.serverId === s.id) ?? false
10706
- };
10707
- });
10708
- const hasEnabledChoices = choices.some((choice) => !choice.disabled);
10709
- const categoryLabel = formatCategory(category);
10710
- if (!hasEnabledChoices) {
10711
- logger.info(colors.muted(`${categoryLabel}: All servers already installed`));
10805
+ const availableInCategory = servers.filter(
10806
+ (s) => !userInstalledSet.has(s.id) && !projectInstalledSet.has(s.id)
10807
+ );
10808
+ if (availableInCategory.length === 0) {
10712
10809
  continue;
10713
10810
  }
10811
+ const choices = availableInCategory.map((s) => ({
10812
+ name: `${s.name} - ${s.description}`,
10813
+ value: s.id,
10814
+ checked: options?.defaults?.servers?.some((i) => i.serverId === s.id) ?? false
10815
+ }));
10816
+ const categoryLabel = formatCategory(category);
10714
10817
  const selected = await checkbox({
10715
10818
  message: `${categoryLabel}:`,
10716
10819
  choices