@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 +319 -216
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +319 -216
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
"
|
|
5508
|
-
"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(
|
|
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
|
|
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
|
|
6062
|
+
let content;
|
|
6080
6063
|
if (options?.customTemplate) {
|
|
6081
|
-
|
|
6064
|
+
content = processCustomTemplate(options.customTemplate, projectInfo);
|
|
6082
6065
|
} else {
|
|
6083
|
-
|
|
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
|
|
6116
|
-
|
|
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
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
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
|
-
|
|
6130
|
-
} else {
|
|
6131
|
-
content = content.replace(/\{\{#if DOMAIN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
6115
|
+
lines.push(`- **Domain:** ${projectInfo.domain}`);
|
|
6132
6116
|
}
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
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
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6146
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
7057
|
-
import * as
|
|
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 =
|
|
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
|
|
8020
|
+
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
7975
8021
|
for (const entry of entries) {
|
|
7976
|
-
const fullPath =
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
10684
|
-
|
|
10685
|
-
|
|
10686
|
-
|
|
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
|