clauderc 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -4
- package/package.json +1 -1
- package/src/detector.js +286 -1
- package/src/project.js +139 -22
package/README.md
CHANGED
|
@@ -12,14 +12,17 @@
|
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
15
|
-
<img src="https://img.shields.io/npm/v/clauderc?color=blue" alt="npm version">
|
|
15
|
+
<a href="https://www.npmjs.com/package/clauderc"><img src="https://img.shields.io/npm/v/clauderc?color=blue" alt="npm version"></a>
|
|
16
16
|
<img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey" alt="platform">
|
|
17
17
|
<img src="https://img.shields.io/badge/node-%3E%3D16.7-green" alt="node version">
|
|
18
|
+
<a href="https://github.com/matheuskindrazki/clauderc/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license"></a>
|
|
19
|
+
<a href="https://github.com/matheuskindrazki/clauderc/blob/main/CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"></a>
|
|
20
|
+
<a href="https://github.com/matheuskindrazki/clauderc/blob/main/CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant"></a>
|
|
18
21
|
</p>
|
|
19
22
|
|
|
20
23
|
---
|
|
21
24
|
|
|
22
|
-
Based on tips from [Boris Cherny](https://twitter.com/bcherny) (Claude Code
|
|
25
|
+
Based on my experiences and tips from [ Boris Cherny ](https://twitter.com/bcherny) (creator of the Claude Code) and official documentation.
|
|
23
26
|
|
|
24
27
|
## Quick Start
|
|
25
28
|
|
|
@@ -198,10 +201,18 @@ After installation:
|
|
|
198
201
|
|
|
199
202
|
## Contributing
|
|
200
203
|
|
|
201
|
-
Contributions are welcome!
|
|
204
|
+
Contributions are welcome! Please read our guidelines before contributing:
|
|
205
|
+
|
|
206
|
+
- **[Contributing Guide](CONTRIBUTING.md)** - How to contribute to the project
|
|
207
|
+
- **[Code of Conduct](CODE_OF_CONDUCT.md)** - Community guidelines
|
|
208
|
+
- **[Security Policy](SECURITY.md)** - How to report vulnerabilities
|
|
209
|
+
|
|
210
|
+
Ways to contribute:
|
|
202
211
|
- Add new commands, skills, or agents
|
|
212
|
+
- Add support for new stacks/languages
|
|
203
213
|
- Improve existing templates
|
|
204
|
-
- Report
|
|
214
|
+
- Report bugs or suggest features
|
|
215
|
+
- Improve documentation
|
|
205
216
|
|
|
206
217
|
## Author
|
|
207
218
|
|
package/package.json
CHANGED
package/src/detector.js
CHANGED
|
@@ -4,8 +4,293 @@
|
|
|
4
4
|
|
|
5
5
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
6
6
|
import { join } from 'path';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
7
8
|
import { STACKS, MONOREPO_TOOLS, CI_PLATFORMS } from './stacks.js';
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Files to read for LLM analysis
|
|
12
|
+
*/
|
|
13
|
+
const ANALYSIS_FILES = [
|
|
14
|
+
'package.json',
|
|
15
|
+
'pyproject.toml',
|
|
16
|
+
'requirements.txt',
|
|
17
|
+
'go.mod',
|
|
18
|
+
'Cargo.toml',
|
|
19
|
+
'composer.json',
|
|
20
|
+
'Gemfile',
|
|
21
|
+
'pom.xml',
|
|
22
|
+
'build.gradle',
|
|
23
|
+
'build.gradle.kts',
|
|
24
|
+
'mix.exs',
|
|
25
|
+
'pubspec.yaml',
|
|
26
|
+
'Package.swift',
|
|
27
|
+
'*.csproj',
|
|
28
|
+
'turbo.json',
|
|
29
|
+
'nx.json',
|
|
30
|
+
'pnpm-workspace.yaml',
|
|
31
|
+
'lerna.json',
|
|
32
|
+
'.github/workflows/*.yml',
|
|
33
|
+
'.gitlab-ci.yml',
|
|
34
|
+
'Jenkinsfile',
|
|
35
|
+
'biome.json',
|
|
36
|
+
'biome.jsonc',
|
|
37
|
+
'.eslintrc*',
|
|
38
|
+
'.prettierrc*',
|
|
39
|
+
'tsconfig.json',
|
|
40
|
+
'vite.config.*',
|
|
41
|
+
'next.config.*',
|
|
42
|
+
'nuxt.config.*',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Read relevant project files for analysis
|
|
47
|
+
*/
|
|
48
|
+
function readProjectFiles(projectPath) {
|
|
49
|
+
const files = {};
|
|
50
|
+
|
|
51
|
+
for (const pattern of ANALYSIS_FILES) {
|
|
52
|
+
if (pattern.includes('*')) {
|
|
53
|
+
// Handle glob patterns
|
|
54
|
+
const parts = pattern.split('/');
|
|
55
|
+
let searchPath = projectPath;
|
|
56
|
+
let filePattern = pattern;
|
|
57
|
+
|
|
58
|
+
if (parts.length > 1) {
|
|
59
|
+
searchPath = join(projectPath, ...parts.slice(0, -1));
|
|
60
|
+
filePattern = parts[parts.length - 1];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (existsSync(searchPath)) {
|
|
64
|
+
const dirFiles = safeReadDir(searchPath);
|
|
65
|
+
const regex = new RegExp('^' + filePattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
|
|
66
|
+
for (const file of dirFiles) {
|
|
67
|
+
if (regex.test(file)) {
|
|
68
|
+
const fullPath = join(searchPath, file);
|
|
69
|
+
const relativePath = fullPath.replace(projectPath + '/', '');
|
|
70
|
+
const content = safeReadFile(fullPath);
|
|
71
|
+
if (content && content.length < 10000) { // Limit file size
|
|
72
|
+
files[relativePath] = content;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
const fullPath = join(projectPath, pattern);
|
|
79
|
+
if (existsSync(fullPath)) {
|
|
80
|
+
const content = safeReadFile(fullPath);
|
|
81
|
+
if (content && content.length < 10000) {
|
|
82
|
+
files[pattern] = content;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return files;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Analyze project using Claude LLM
|
|
93
|
+
* @param {string} projectPath - Path to the project
|
|
94
|
+
* @returns {Object} - Detection result compatible with detectStack output
|
|
95
|
+
*/
|
|
96
|
+
export function analyzeWithClaude(projectPath = process.cwd()) {
|
|
97
|
+
const files = readProjectFiles(projectPath);
|
|
98
|
+
|
|
99
|
+
if (Object.keys(files).length === 0) {
|
|
100
|
+
console.warn(' ⚠ No project files found for analysis');
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const filesContent = Object.entries(files)
|
|
105
|
+
.map(([name, content]) => `=== ${name} ===\n${content}`)
|
|
106
|
+
.join('\n\n');
|
|
107
|
+
|
|
108
|
+
const prompt = `Analyze this project and return a JSON configuration for Claude Code setup.
|
|
109
|
+
|
|
110
|
+
PROJECT FILES:
|
|
111
|
+
${filesContent}
|
|
112
|
+
|
|
113
|
+
Return ONLY valid JSON (no markdown, no explanation) with this exact structure:
|
|
114
|
+
{
|
|
115
|
+
"stack": {
|
|
116
|
+
"language": "Node.js|Python|Go|Rust|Java|PHP|Ruby|.NET|Elixir|Dart|Swift",
|
|
117
|
+
"framework": "Next.js|Nuxt|React|Vue|FastAPI|Django|Flask|Express|Gin|Rails|Laravel|Phoenix|etc or null",
|
|
118
|
+
"packageManager": "npm|pnpm|bun|yarn|pip|poetry|uv|cargo|go|maven|gradle|composer|bundler|mix|etc",
|
|
119
|
+
"monorepo": "turborepo|nx|lerna|pnpm-workspaces|yarn-workspaces or null",
|
|
120
|
+
"testFramework": "vitest|jest|pytest|go test|cargo test|phpunit|rspec|etc or null",
|
|
121
|
+
"linter": "eslint|biome|ruff|golangci-lint|clippy|rubocop|etc or null",
|
|
122
|
+
"formatter": "prettier|biome|black|ruff|gofmt|rustfmt|etc or null",
|
|
123
|
+
"ci": "github-actions|gitlab-ci|jenkins|circleci or null"
|
|
124
|
+
},
|
|
125
|
+
"commands": {
|
|
126
|
+
"setup": "command to install dependencies",
|
|
127
|
+
"dev": "command to run dev server",
|
|
128
|
+
"test": "command to run tests",
|
|
129
|
+
"lint": "command to lint code",
|
|
130
|
+
"format": "command to format code",
|
|
131
|
+
"typecheck": "command to check types or null",
|
|
132
|
+
"build": "command to build or null",
|
|
133
|
+
"verify": "combined lint + test + build command"
|
|
134
|
+
},
|
|
135
|
+
"preferences": {
|
|
136
|
+
"notes": "any special preferences detected (e.g., prefers bun over npm, uses specific conventions)"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
IMPORTANT:
|
|
141
|
+
- Detect ACTUAL tools being used, not defaults
|
|
142
|
+
- If bun.lockb exists, use bun commands
|
|
143
|
+
- If pnpm-lock.yaml exists, use pnpm commands
|
|
144
|
+
- If uv.lock exists, use uv commands
|
|
145
|
+
- Look at scripts in package.json for exact command names
|
|
146
|
+
- Return commands that will actually work for this project`;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// Escape the prompt for shell
|
|
150
|
+
const escapedPrompt = prompt
|
|
151
|
+
.replace(/\\/g, '\\\\')
|
|
152
|
+
.replace(/"/g, '\\"')
|
|
153
|
+
.replace(/\$/g, '\\$')
|
|
154
|
+
.replace(/`/g, '\\`');
|
|
155
|
+
|
|
156
|
+
const result = execSync(
|
|
157
|
+
`claude -p --model haiku "${escapedPrompt}"`,
|
|
158
|
+
{
|
|
159
|
+
encoding: 'utf-8',
|
|
160
|
+
maxBuffer: 1024 * 1024 * 10,
|
|
161
|
+
timeout: 120000, // 2 minute timeout
|
|
162
|
+
cwd: projectPath,
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Extract JSON from response
|
|
167
|
+
const jsonMatch = result.match(/\{[\s\S]*\}/);
|
|
168
|
+
if (!jsonMatch) {
|
|
169
|
+
console.warn(' ⚠ Could not parse Claude response');
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const analysis = JSON.parse(jsonMatch[0]);
|
|
174
|
+
|
|
175
|
+
// Convert to detectStack compatible format
|
|
176
|
+
return {
|
|
177
|
+
analysis,
|
|
178
|
+
stack: convertToStackFormat(analysis.stack),
|
|
179
|
+
commands: analysis.commands,
|
|
180
|
+
preferences: analysis.preferences,
|
|
181
|
+
};
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.warn(' ⚠ Claude analysis failed:', error.message);
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Convert LLM analysis to detectStack format
|
|
190
|
+
*/
|
|
191
|
+
function convertToStackFormat(llmStack) {
|
|
192
|
+
const result = {
|
|
193
|
+
stacks: [],
|
|
194
|
+
packageManager: null,
|
|
195
|
+
framework: null,
|
|
196
|
+
monorepo: null,
|
|
197
|
+
ci: null,
|
|
198
|
+
testFramework: null,
|
|
199
|
+
linter: null,
|
|
200
|
+
formatter: null,
|
|
201
|
+
typechecker: null,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Map language to stack
|
|
205
|
+
const languageMap = {
|
|
206
|
+
'Node.js': 'node',
|
|
207
|
+
'Python': 'python',
|
|
208
|
+
'Go': 'go',
|
|
209
|
+
'Rust': 'rust',
|
|
210
|
+
'Java': 'java',
|
|
211
|
+
'PHP': 'php',
|
|
212
|
+
'Ruby': 'ruby',
|
|
213
|
+
'.NET': 'dotnet',
|
|
214
|
+
'Elixir': 'elixir',
|
|
215
|
+
'Dart': 'dart',
|
|
216
|
+
'Swift': 'swift',
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const stackId = languageMap[llmStack.language];
|
|
220
|
+
if (stackId && STACKS[stackId]) {
|
|
221
|
+
result.stacks.push({ id: stackId, name: llmStack.language, ...STACKS[stackId] });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Package manager
|
|
225
|
+
if (llmStack.packageManager) {
|
|
226
|
+
result.packageManager = {
|
|
227
|
+
name: llmStack.packageManager,
|
|
228
|
+
install: getInstallCommand(llmStack.packageManager),
|
|
229
|
+
run: getRunCommand(llmStack.packageManager),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Framework
|
|
234
|
+
if (llmStack.framework) {
|
|
235
|
+
result.framework = { name: llmStack.framework };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Monorepo
|
|
239
|
+
if (llmStack.monorepo) {
|
|
240
|
+
result.monorepo = { name: llmStack.monorepo };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// CI
|
|
244
|
+
if (llmStack.ci) {
|
|
245
|
+
result.ci = { name: llmStack.ci };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Tools
|
|
249
|
+
result.testFramework = llmStack.testFramework;
|
|
250
|
+
result.linter = llmStack.linter;
|
|
251
|
+
result.formatter = llmStack.formatter;
|
|
252
|
+
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get install command for package manager
|
|
258
|
+
*/
|
|
259
|
+
function getInstallCommand(pm) {
|
|
260
|
+
const commands = {
|
|
261
|
+
npm: 'npm install',
|
|
262
|
+
pnpm: 'pnpm install',
|
|
263
|
+
bun: 'bun install',
|
|
264
|
+
yarn: 'yarn install',
|
|
265
|
+
pip: 'pip install -r requirements.txt',
|
|
266
|
+
poetry: 'poetry install',
|
|
267
|
+
uv: 'uv sync',
|
|
268
|
+
cargo: 'cargo build',
|
|
269
|
+
go: 'go mod download',
|
|
270
|
+
maven: 'mvn install -DskipTests',
|
|
271
|
+
gradle: 'gradle build -x test',
|
|
272
|
+
composer: 'composer install',
|
|
273
|
+
bundler: 'bundle install',
|
|
274
|
+
mix: 'mix deps.get',
|
|
275
|
+
};
|
|
276
|
+
return commands[pm] || `${pm} install`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get run command prefix for package manager
|
|
281
|
+
*/
|
|
282
|
+
function getRunCommand(pm) {
|
|
283
|
+
const commands = {
|
|
284
|
+
npm: 'npm run',
|
|
285
|
+
pnpm: 'pnpm',
|
|
286
|
+
bun: 'bun run',
|
|
287
|
+
yarn: 'yarn',
|
|
288
|
+
poetry: 'poetry run',
|
|
289
|
+
uv: 'uv run',
|
|
290
|
+
};
|
|
291
|
+
return commands[pm] || '';
|
|
292
|
+
}
|
|
293
|
+
|
|
9
294
|
/**
|
|
10
295
|
* Detect project stack from current directory
|
|
11
296
|
*/
|
|
@@ -384,4 +669,4 @@ export function generateCommands(detection) {
|
|
|
384
669
|
return commands;
|
|
385
670
|
}
|
|
386
671
|
|
|
387
|
-
export default { detectStack, generateCommands };
|
|
672
|
+
export default { detectStack, generateCommands, analyzeWithClaude };
|
package/src/project.js
CHANGED
|
@@ -5,7 +5,74 @@
|
|
|
5
5
|
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
6
6
|
import { join, basename } from 'path';
|
|
7
7
|
import { createInterface } from 'readline';
|
|
8
|
-
import {
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { detectStack, generateCommands, analyzeWithClaude } from './detector.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Use Claude CLI to intelligently merge existing and new content
|
|
13
|
+
* @param {string} existingContent - The current file content
|
|
14
|
+
* @param {string} newContent - The proposed new content
|
|
15
|
+
* @param {string} fileType - Type of file for context (e.g., 'CLAUDE.md', 'settings.json')
|
|
16
|
+
* @returns {string} - The merged content
|
|
17
|
+
*/
|
|
18
|
+
function mergeWithClaude(existingContent, newContent, fileType) {
|
|
19
|
+
const prompt = `You are merging two versions of a ${fileType} configuration file.
|
|
20
|
+
|
|
21
|
+
EXISTING content (user's current configuration - PRESERVE user preferences and customizations):
|
|
22
|
+
---
|
|
23
|
+
${existingContent}
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
NEW content (auto-generated with updated stack detection):
|
|
27
|
+
---
|
|
28
|
+
${newContent}
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
IMPORTANT RULES:
|
|
32
|
+
1. PRESERVE any custom rules, preferences, or configurations the user added in EXISTING
|
|
33
|
+
2. UPDATE commands and stack info from NEW if they are more accurate
|
|
34
|
+
3. MERGE sections intelligently - don't duplicate, combine them
|
|
35
|
+
4. Keep user's custom sections that don't exist in NEW
|
|
36
|
+
5. If EXISTING has custom hooks, permissions, or rules - KEEP THEM
|
|
37
|
+
6. Output ONLY the merged content, no explanations
|
|
38
|
+
|
|
39
|
+
Output the merged ${fileType}:`;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Use Claude CLI in print mode for non-interactive merge
|
|
43
|
+
const result = execSync(
|
|
44
|
+
`claude -p --model haiku "${prompt.replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`,
|
|
45
|
+
{
|
|
46
|
+
encoding: 'utf-8',
|
|
47
|
+
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
|
|
48
|
+
timeout: 60000, // 60 second timeout
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
return result.trim();
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// If Claude CLI fails, return new content with a warning
|
|
54
|
+
console.warn(' ⚠ Could not use Claude for merge, using new content');
|
|
55
|
+
return newContent;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Smart write that merges with existing content if file exists
|
|
61
|
+
* @param {string} filePath - Path to the file
|
|
62
|
+
* @param {string} newContent - New content to write
|
|
63
|
+
* @param {string} fileType - Type of file for merge context
|
|
64
|
+
* @param {boolean} useMerge - Whether to use Claude merge
|
|
65
|
+
*/
|
|
66
|
+
function smartWrite(filePath, newContent, fileType, useMerge = true) {
|
|
67
|
+
if (useMerge && existsSync(filePath)) {
|
|
68
|
+
const existingContent = readFileSync(filePath, 'utf-8');
|
|
69
|
+
const mergedContent = mergeWithClaude(existingContent, newContent, fileType);
|
|
70
|
+
writeFileSync(filePath, mergedContent);
|
|
71
|
+
return { merged: true, path: filePath };
|
|
72
|
+
}
|
|
73
|
+
writeFileSync(filePath, newContent);
|
|
74
|
+
return { merged: false, path: filePath };
|
|
75
|
+
}
|
|
9
76
|
|
|
10
77
|
/**
|
|
11
78
|
* Interactive prompt helper
|
|
@@ -307,11 +374,37 @@ export async function runProjectWizard(options = {}) {
|
|
|
307
374
|
const prompt = createPrompt();
|
|
308
375
|
|
|
309
376
|
try {
|
|
310
|
-
log('\n
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const
|
|
377
|
+
log('\n Claude Code Project Setup\n');
|
|
378
|
+
log(' This wizard will configure Claude Code for your project.\n');
|
|
379
|
+
|
|
380
|
+
// Ask about AI analysis
|
|
381
|
+
const useAI = await prompt.confirm(' Use Claude AI for smarter detection? (recommended)');
|
|
382
|
+
|
|
383
|
+
let stack;
|
|
384
|
+
let commands;
|
|
385
|
+
let aiAnalysis = null;
|
|
386
|
+
|
|
387
|
+
if (useAI) {
|
|
388
|
+
log('\n Analyzing project with Claude AI...\n');
|
|
389
|
+
aiAnalysis = analyzeWithClaude(projectPath);
|
|
390
|
+
|
|
391
|
+
if (aiAnalysis) {
|
|
392
|
+
stack = aiAnalysis.stack;
|
|
393
|
+
commands = aiAnalysis.commands;
|
|
394
|
+
|
|
395
|
+
if (aiAnalysis.preferences?.notes) {
|
|
396
|
+
log(` Notes: ${aiAnalysis.preferences.notes}\n`);
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
log(' AI analysis failed, falling back to deterministic detection...\n');
|
|
400
|
+
stack = detectStack(projectPath);
|
|
401
|
+
commands = generateCommands(stack);
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
log('\n Analyzing project...\n');
|
|
405
|
+
stack = detectStack(projectPath);
|
|
406
|
+
commands = generateCommands(stack);
|
|
407
|
+
}
|
|
315
408
|
|
|
316
409
|
// Show detection results
|
|
317
410
|
log(' Detected configuration:\n');
|
|
@@ -375,20 +468,35 @@ export async function runProjectWizard(options = {}) {
|
|
|
375
468
|
// Check existing .claude directory
|
|
376
469
|
const claudeDir = join(projectPath, '.claude');
|
|
377
470
|
const hasExisting = existsSync(claudeDir);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
471
|
+
const hasExistingClaudeMd = existsSync(join(projectPath, 'CLAUDE.md'));
|
|
472
|
+
let useMerge = false;
|
|
473
|
+
|
|
474
|
+
if (hasExisting || hasExistingClaudeMd) {
|
|
475
|
+
log('\n Existing configuration detected.\n');
|
|
476
|
+
const mergeChoice = await prompt.select(' How would you like to proceed?', [
|
|
477
|
+
{ label: 'Merge', description: 'Use Claude to intelligently merge with existing config (recommended)' },
|
|
478
|
+
{ label: 'Overwrite', description: 'Replace all existing configuration' },
|
|
479
|
+
{ label: 'Cancel', description: 'Keep existing configuration unchanged' },
|
|
480
|
+
]);
|
|
481
|
+
|
|
482
|
+
if (mergeChoice.label === 'Cancel') {
|
|
382
483
|
log('\n Setup cancelled.\n');
|
|
383
484
|
prompt.close();
|
|
384
485
|
return null;
|
|
385
486
|
}
|
|
487
|
+
|
|
488
|
+
useMerge = mergeChoice.label === 'Merge';
|
|
489
|
+
|
|
490
|
+
if (useMerge) {
|
|
491
|
+
log('\n Will use Claude to merge configurations intelligently.\n');
|
|
492
|
+
}
|
|
386
493
|
}
|
|
387
494
|
|
|
388
495
|
prompt.close();
|
|
389
496
|
|
|
390
497
|
if (dryRun) {
|
|
391
|
-
|
|
498
|
+
const action = useMerge ? 'merge with' : 'create';
|
|
499
|
+
log(`\n DRY RUN - Would ${action}:\n`);
|
|
392
500
|
log(' .claude/');
|
|
393
501
|
log(' ├── commands/test.md');
|
|
394
502
|
log(' ├── commands/lint.md');
|
|
@@ -396,30 +504,39 @@ export async function runProjectWizard(options = {}) {
|
|
|
396
504
|
log(' ├── commands/setup.md');
|
|
397
505
|
log(' ├── commands/pr.md');
|
|
398
506
|
log(' ├── settings.json');
|
|
399
|
-
log(' CLAUDE.md
|
|
507
|
+
log(' CLAUDE.md');
|
|
508
|
+
if (useMerge) {
|
|
509
|
+
log('\n Note: Claude will intelligently merge with existing files.\n');
|
|
510
|
+
}
|
|
400
511
|
return config;
|
|
401
512
|
}
|
|
402
513
|
|
|
403
514
|
// Create directories
|
|
404
515
|
mkdirSync(join(claudeDir, 'commands'), { recursive: true });
|
|
405
516
|
|
|
406
|
-
// Write files
|
|
517
|
+
// Write files with smart merge
|
|
407
518
|
const files = [
|
|
408
|
-
{ path: join(projectPath, 'CLAUDE.md'), content: generateClaudeMd(config) },
|
|
409
|
-
{ path: join(claudeDir, 'settings.json'), content: JSON.stringify(generateSettings(config), null, 2) },
|
|
410
|
-
{ path: join(claudeDir, 'commands', 'test.md'), content: generateCommandFile('test', config) },
|
|
411
|
-
{ path: join(claudeDir, 'commands', 'lint.md'), content: generateCommandFile('lint', config) },
|
|
412
|
-
{ path: join(claudeDir, 'commands', 'verify.md'), content: generateCommandFile('verify', config) },
|
|
413
|
-
{ path: join(claudeDir, 'commands', 'setup.md'), content: generateCommandFile('setup', config) },
|
|
414
|
-
{ path: join(claudeDir, 'commands', 'pr.md'), content: generateCommandFile('pr', config) },
|
|
519
|
+
{ path: join(projectPath, 'CLAUDE.md'), content: generateClaudeMd(config), type: 'CLAUDE.md' },
|
|
520
|
+
{ path: join(claudeDir, 'settings.json'), content: JSON.stringify(generateSettings(config), null, 2), type: 'settings.json' },
|
|
521
|
+
{ path: join(claudeDir, 'commands', 'test.md'), content: generateCommandFile('test', config), type: 'command file' },
|
|
522
|
+
{ path: join(claudeDir, 'commands', 'lint.md'), content: generateCommandFile('lint', config), type: 'command file' },
|
|
523
|
+
{ path: join(claudeDir, 'commands', 'verify.md'), content: generateCommandFile('verify', config), type: 'command file' },
|
|
524
|
+
{ path: join(claudeDir, 'commands', 'setup.md'), content: generateCommandFile('setup', config), type: 'command file' },
|
|
525
|
+
{ path: join(claudeDir, 'commands', 'pr.md'), content: generateCommandFile('pr', config), type: 'command file' },
|
|
415
526
|
];
|
|
416
527
|
|
|
417
528
|
for (const file of files) {
|
|
418
|
-
|
|
419
|
-
|
|
529
|
+
const result = smartWrite(file.path, file.content, file.type, useMerge);
|
|
530
|
+
const symbol = result.merged ? '~' : '+';
|
|
531
|
+
const action = result.merged ? 'merged' : 'created';
|
|
532
|
+
log(` ${symbol} ${file.path.replace(projectPath, '.')} (${action})`);
|
|
420
533
|
}
|
|
421
534
|
|
|
422
535
|
log('\n Project setup complete!\n');
|
|
536
|
+
if (useMerge) {
|
|
537
|
+
log(' Files were merged with existing configuration.');
|
|
538
|
+
log(' Your custom settings and preferences were preserved.\n');
|
|
539
|
+
}
|
|
423
540
|
log(' Next steps:');
|
|
424
541
|
log(' 1. Review CLAUDE.md and adjust as needed');
|
|
425
542
|
log(' 2. Commit .claude/ to your repository');
|