claude-config-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +177 -0
  3. package/cli.js +378 -0
  4. package/package.json +48 -0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 KazooTTT
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # Claude Config CLI
2
+
3
+ [![npm version][npm-version-src]][npm-version-href]
4
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
+ [![License][license-src]][license-href]
6
+
7
+ A CLI tool to manage Claude Code configuration files by scanning existing configs and applying recommended settings.
8
+
9
+ ## Features
10
+
11
+ - **Scan Mode**: Scan multiple directories to find existing Claude config files and generate a recommended configuration
12
+ - **Apply Mode**: Directly apply a pre-defined recommended configuration
13
+ - **Merge or Overwrite**: Choose to merge with existing configs or overwrite them
14
+ - **Interactive CLI**: User-friendly prompts for all operations
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # Using npm
20
+ npm install -g claude-config-cli
21
+
22
+ # Using pnpm
23
+ pnpm add -g claude-config-cli
24
+
25
+ # Using yarn
26
+ yarn global add claude-config-cli
27
+ ```
28
+
29
+ Or use directly with npx (no installation needed):
30
+
31
+ ```bash
32
+ npx claude-config-cli
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ### Command: `scan`
38
+
39
+ Scan directories for Claude config files and generate recommended configuration.
40
+
41
+ ```bash
42
+ # Scan current directory
43
+ claude-config scan
44
+
45
+ # Scan specific directories
46
+ claude-config scan ~/personal ~/work
47
+
48
+ # Preview without applying
49
+ claude-config scan --preview
50
+
51
+ # Specify output path
52
+ claude-config scan -o .claude/settings.local.json
53
+ ```
54
+
55
+ ### Command: `apply`
56
+
57
+ Apply the recommended Claude configuration directly.
58
+
59
+ ```bash
60
+ # Apply with interactive prompts
61
+ claude-config apply
62
+
63
+ # Preview the recommended config
64
+ claude-config apply --preview
65
+
66
+ # Specify output path
67
+ claude-config apply -o /custom/path/settings.json
68
+ ```
69
+
70
+ ### Options
71
+
72
+ Both commands support the following options:
73
+
74
+ - `-o, --output <path>`: Specify the output file path (default: `.claude/settings.local.json`)
75
+ - `-p, --preview`: Preview the configuration without applying it
76
+ - `-h, --help`: Display help information
77
+
78
+ ### Interactive Prompts
79
+
80
+ When you run `scan` or `apply` without `--preview`, you'll be prompted to choose an action:
81
+
82
+ - **Save to new file**: Create a new config file
83
+ - **Merge with existing config**: Merge the recommended config with any existing config (preserves existing settings)
84
+ - **Overwrite existing config**: Replace any existing config with the recommended one
85
+ - **Cancel**: Cancel the operation
86
+
87
+ ## Example Workflow
88
+
89
+ ### 1. Scan and Preview
90
+
91
+ ```bash
92
+ npx claude-config scan ~/personal ~/work --preview
93
+ ```
94
+
95
+ This will show you the recommended configuration based on all configs found in the specified directories.
96
+
97
+ ### 2. Apply Recommended Config
98
+
99
+ ```bash
100
+ npx claude-config apply
101
+ ```
102
+
103
+ You'll be prompted to choose how to apply the config. Select your preferred option and the tool will handle the rest.
104
+
105
+ ### 3. Scan and Apply in One Step
106
+
107
+ ```bash
108
+ npx claude-config scan ~/projects
109
+ ```
110
+
111
+ After scanning, choose how you want to apply the generated configuration.
112
+
113
+ ## Recommended Configuration
114
+
115
+ The recommended configuration includes common permissions for:
116
+
117
+ - Package managers (pnpm, npm, bun)
118
+ - Build tools (cargo, wrangler)
119
+ - Git operations
120
+ - Development tools (node, python, tsc)
121
+ - MCP tools (context7, ast-grep, ide)
122
+
123
+ This configuration is based on common patterns found across many projects.
124
+
125
+ ## Development
126
+
127
+ ### Local Testing
128
+
129
+ ```bash
130
+ # Install dependencies
131
+ pnpm install
132
+
133
+ # Test the CLI
134
+ node cli.js --help
135
+ node cli.js scan --preview
136
+ node cli.js apply --preview
137
+
138
+ # Run tests
139
+ pnpm test
140
+ ```
141
+
142
+ ### Publishing to npm
143
+
144
+ ```bash
145
+ # Build (if you have a build step)
146
+ pnpm run build
147
+
148
+ # Publish
149
+ pnpm publish
150
+ ```
151
+
152
+ ## Release
153
+
154
+ This project uses [bumpp](https://github.com/antfu/bumpp) for version management.
155
+
156
+ ```bash
157
+ # Interactive release
158
+ pnpm release
159
+
160
+ # Automatic release (patch/minor/major)
161
+ pnpm release:patch
162
+ pnpm release:minor
163
+ pnpm release:major
164
+ ```
165
+
166
+ ## License
167
+
168
+ [MIT](./LICENSE) License
169
+
170
+ <!-- Badges -->
171
+
172
+ [npm-version-src]: https://img.shields.io/npm/v/claude-config-cli?style=flat&colorA=080f12&colorB=1fa669
173
+ [npm-version-href]: https://npmjs.com/package/claude-config-cli
174
+ [npm-downloads-src]: https://img.shields.io/npm/dm/claude-config-cli?style=flat&colorA=080f12&colorB=1fa669
175
+ [npm-downloads-href]: https://npmjs.com/package/claude-config-cli
176
+ [license-src]: https://img.shields.io/github/license/kazoottt/claude-config-generate.svg?style=flat&colorA=080f12&colorB=1fa669
177
+ [license-href]: https://github.com/kazoottt/claude-config-generate/blob/main/LICENSE
package/cli.js ADDED
@@ -0,0 +1,378 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import inquirer from 'inquirer';
5
+ import chalk from 'chalk';
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ // 预定义的推荐配置
14
+ const RECOMMENDED_CONFIG = {
15
+ permissions: {
16
+ allow: [
17
+ "Bash(pnpm build:*)",
18
+ "WebSearch",
19
+ "Bash(find:*)",
20
+ "Bash(npm run build:*)",
21
+ "Bash(pnpm add:*)",
22
+ "Bash(pnpm typecheck:*)",
23
+ "Bash(pnpm lint:*)",
24
+ "Bash(cargo check:*)",
25
+ "Bash(wrangler d1 execute:*)",
26
+ "Bash(git diff:*)",
27
+ "Bash(xargs:*)",
28
+ "Bash(python:*)",
29
+ "Bash(mv:*)",
30
+ "Bash(npm run lint)",
31
+ "Bash(node:*)",
32
+ "Bash(git branch:*)",
33
+ "mcp__context7__resolvelibraryid",
34
+ "mcp__ide__getDiagnostics",
35
+ "Bash(npm run dev:*)",
36
+ "Bash(bunx:*)",
37
+ "Bash(pnpm run:*)",
38
+ "Bash(npx tsc:*)",
39
+ "mcp__astgrep__find_code",
40
+ "Bash(python3:*)",
41
+ "Bash(chmod:*)"
42
+ ],
43
+ deny: [],
44
+ ask: []
45
+ }
46
+ };
47
+
48
+ const program = new Command();
49
+
50
+ program
51
+ .name('claude-config')
52
+ .description('CLI tool to manage Claude Code configuration files')
53
+ .version('1.0.0');
54
+
55
+ // 扫描命令
56
+ program
57
+ .command('scan')
58
+ .description('Scan directories for Claude config files and generate recommended config')
59
+ .argument('[dirs...]', 'Directories to scan (default: current directory)')
60
+ .option('-o, --output <path>', 'Output file path (default: .claude/settings.local.json)')
61
+ .option('-p, --preview', 'Preview the recommended config without applying')
62
+ .action(async (dirs, options) => {
63
+ const scanDirs = dirs.length > 0 ? dirs : [process.cwd()];
64
+ const outputPath = options.output || path.join(process.cwd(), '.claude/settings.local.json');
65
+
66
+ console.log(chalk.blue('🔍 Scanning directories for Claude config files...'));
67
+ scanDirs.forEach(dir => console.log(chalk.gray(` ${dir}`)));
68
+
69
+ const configFiles = findConfigFiles(scanDirs);
70
+ console.log(chalk.blue(`\n📊 Found ${configFiles.length} config file(s)\n`));
71
+
72
+ if (configFiles.length === 0) {
73
+ console.log(chalk.yellow('⚠️ No config files found. Use the "apply" command to use the recommended config.'));
74
+ return;
75
+ }
76
+
77
+ const stats = scanConfigs(configFiles);
78
+ const recommended = generateRecommendedConfig(stats);
79
+
80
+ if (options.preview) {
81
+ console.log(chalk.green('📋 Recommended Configuration:\n'));
82
+ console.log(JSON.stringify(recommended, null, 2));
83
+ return;
84
+ }
85
+
86
+ // Ask user what to do
87
+ const { action } = await inquirer.prompt([
88
+ {
89
+ type: 'list',
90
+ name: 'action',
91
+ message: 'What would you like to do?',
92
+ choices: [
93
+ { name: '💾 Save to new file', value: 'save' },
94
+ { name: '🔀 Merge with existing config (if exists)', value: 'merge' },
95
+ { name: '♻️ Overwrite existing config (if exists)', value: 'overwrite' },
96
+ { name: '❌ Cancel', value: 'cancel' }
97
+ ]
98
+ }
99
+ ]);
100
+
101
+ if (action === 'cancel') {
102
+ console.log(chalk.gray('Operation cancelled.'));
103
+ return;
104
+ }
105
+
106
+ await applyConfig(recommended, outputPath, action);
107
+ });
108
+
109
+ // 应用命令
110
+ program
111
+ .command('apply')
112
+ .description('Apply the recommended Claude config')
113
+ .option('-o, --output <path>', 'Output file path (default: .claude/settings.local.json)')
114
+ .option('-p, --preview', 'Preview the recommended config without applying')
115
+ .action(async (options) => {
116
+ const outputPath = options.output || path.join(process.cwd(), '.claude/settings.local.json');
117
+
118
+ if (options.preview) {
119
+ console.log(chalk.green('📋 Recommended Configuration:\n'));
120
+ console.log(JSON.stringify(RECOMMENDED_CONFIG, null, 2));
121
+ return;
122
+ }
123
+
124
+ console.log(chalk.green('🚀 Applying recommended Claude configuration...\n'));
125
+
126
+ const { action } = await inquirer.prompt([
127
+ {
128
+ type: 'list',
129
+ name: 'action',
130
+ message: 'How would you like to apply the config?',
131
+ choices: [
132
+ { name: '💾 Save to new file', value: 'save' },
133
+ { name: '🔀 Merge with existing config (if exists)', value: 'merge' },
134
+ { name: '♻️ Overwrite existing config (if exists)', value: 'overwrite' },
135
+ { name: '❌ Cancel', value: 'cancel' }
136
+ ]
137
+ }
138
+ ]);
139
+
140
+ if (action === 'cancel') {
141
+ console.log(chalk.gray('Operation cancelled.'));
142
+ return;
143
+ }
144
+
145
+ await applyConfig(RECOMMENDED_CONFIG, outputPath, action);
146
+ });
147
+
148
+ /**
149
+ * 查找配置文件
150
+ */
151
+ function findConfigFiles(dirs) {
152
+ const CONFIG_PATHS = [
153
+ '.claude/settings.local.json',
154
+ '@.claude/settings.local.json',
155
+ '.claude.json',
156
+ ];
157
+
158
+ const files = [];
159
+
160
+ for (const dir of dirs) {
161
+ if (!fs.existsSync(dir)) continue;
162
+
163
+ try {
164
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
165
+
166
+ for (const entry of entries) {
167
+ const fullPath = path.join(dir, entry.name);
168
+
169
+ // 跳过隐藏目录(除了 .claude)
170
+ if (entry.name.startsWith('.') && entry.name !== '.claude') {
171
+ continue;
172
+ }
173
+
174
+ // 跳过 node_modules
175
+ if (entry.name === 'node_modules' || entry.name === '.git') {
176
+ continue;
177
+ }
178
+
179
+ // 检查是否是配置文件
180
+ for (const configPath of CONFIG_PATHS) {
181
+ const configFile = path.join(fullPath, configPath);
182
+ if (fs.existsSync(configFile)) {
183
+ files.push(configFile);
184
+ }
185
+ }
186
+
187
+ // 递归查找子目录
188
+ if (entry.isDirectory()) {
189
+ files.push(...findConfigFiles([fullPath]));
190
+ }
191
+ }
192
+ } catch (err) {
193
+ // 忽略权限错误
194
+ }
195
+ }
196
+
197
+ return files;
198
+ }
199
+
200
+ /**
201
+ * 扫描配置文件并统计
202
+ */
203
+ function scanConfigs(configFiles) {
204
+ const stats = {
205
+ permissions: { allow: new Map(), deny: new Map(), ask: new Map() },
206
+ allowToRun: new Map(),
207
+ allowedCommands: new Map(),
208
+ allowedFileOperations: new Map(),
209
+ excludedPaths: new Map(),
210
+ };
211
+
212
+ for (const filePath of configFiles) {
213
+ try {
214
+ const content = fs.readFileSync(filePath, 'utf8');
215
+ const config = JSON.parse(content);
216
+
217
+ // 统计 permissions
218
+ if (config.permissions) {
219
+ ['allow', 'deny', 'ask'].forEach(type => {
220
+ if (Array.isArray(config.permissions[type])) {
221
+ config.permissions[type].forEach(item => {
222
+ const count = stats.permissions[type].get(item) || 0;
223
+ stats.permissions[type].set(item, count + 1);
224
+ });
225
+ }
226
+ });
227
+ }
228
+
229
+ // 统计其他配置项
230
+ [
231
+ 'allowToRun',
232
+ 'allowedCommands',
233
+ 'allowedFileOperations',
234
+ 'excludedPaths'
235
+ ].forEach(key => {
236
+ if (Array.isArray(config[key])) {
237
+ config[key].forEach(item => {
238
+ const count = stats[key].get(item) || 0;
239
+ stats[key].set(item, count + 1);
240
+ });
241
+ }
242
+ });
243
+
244
+ } catch (err) {
245
+ console.error(chalk.red(`❌ Failed to parse ${filePath}`));
246
+ }
247
+ }
248
+
249
+ return stats;
250
+ }
251
+
252
+ /**
253
+ * 生成推荐配置(过滤特定路径)
254
+ */
255
+ function generateRecommendedConfig(stats) {
256
+ const isGlobal = (item) => {
257
+ if (typeof item !== 'string') return true;
258
+ return !item.includes('/Users/') && !item.includes('/home/');
259
+ };
260
+
261
+ const recommended = {};
262
+
263
+ // 收集 permissions.allow
264
+ const allAllow = Array.from(stats.permissions.allow.entries())
265
+ .filter(([item]) => isGlobal(item))
266
+ .filter(([_, count]) => count >= 2)
267
+ .sort((a, b) => b[1] - a[1])
268
+ .map(([item]) => item);
269
+
270
+ if (allAllow.length > 0) {
271
+ recommended.permissions = {
272
+ allow: allAllow,
273
+ deny: [],
274
+ ask: []
275
+ };
276
+ }
277
+
278
+ // 收集其他配置项
279
+ [
280
+ 'allowToRun',
281
+ 'allowedCommands',
282
+ 'allowedFileOperations',
283
+ 'excludedPaths'
284
+ ].forEach(key => {
285
+ const items = Array.from(stats[key].entries())
286
+ .filter(([item]) => isGlobal(item))
287
+ .filter(([_, count]) => count >= 2)
288
+ .sort((a, b) => b[1] - a[1])
289
+ .map(([item]) => item);
290
+
291
+ if (items.length > 0) {
292
+ recommended[key] = items;
293
+ }
294
+ });
295
+
296
+ return recommended;
297
+ }
298
+
299
+ /**
300
+ * 应用配置
301
+ */
302
+ async function applyConfig(newConfig, outputPath, action) {
303
+ const dir = path.dirname(outputPath);
304
+
305
+ // 创建目录
306
+ if (!fs.existsSync(dir)) {
307
+ fs.mkdirSync(dir, { recursive: true });
308
+ }
309
+
310
+ let finalConfig = newConfig;
311
+
312
+ // 处理合并模式
313
+ if (action === 'merge' && fs.existsSync(outputPath)) {
314
+ try {
315
+ const existing = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
316
+ finalConfig = mergeConfigs(existing, newConfig);
317
+ console.log(chalk.blue('🔀 Merging with existing configuration...'));
318
+ } catch (err) {
319
+ console.error(chalk.red('❌ Failed to parse existing config, using new config only'));
320
+ }
321
+ }
322
+
323
+ // 写入文件
324
+ fs.writeFileSync(outputPath, JSON.stringify(finalConfig, null, 2), 'utf8');
325
+
326
+ console.log(chalk.green(`✅ Configuration ${action === 'merge' ? 'merged' : 'applied'} successfully!`));
327
+ console.log(chalk.gray(` Location: ${outputPath}\n`));
328
+
329
+ // 显示预览
330
+ console.log(chalk.blue('📋 Applied Configuration:\n'));
331
+ console.log(JSON.stringify(finalConfig, null, 2));
332
+ }
333
+
334
+ /**
335
+ * 合并配置(去重)
336
+ */
337
+ function mergeConfigs(existing, newConfig) {
338
+ const merged = { ...existing };
339
+
340
+ // 合并 permissions
341
+ if (newConfig.permissions) {
342
+ if (!merged.permissions) {
343
+ merged.permissions = {};
344
+ }
345
+
346
+ ['allow', 'deny', 'ask'].forEach(type => {
347
+ if (newConfig.permissions[type]) {
348
+ const existingItems = merged.permissions[type] || [];
349
+ const newItems = newConfig.permissions[type];
350
+
351
+ // 合并并去重
352
+ const combined = [...new Set([...existingItems, ...newItems])];
353
+ merged.permissions[type] = combined;
354
+ }
355
+ });
356
+ }
357
+
358
+ // 合并数组配置项
359
+ [
360
+ 'allowToRun',
361
+ 'allowedCommands',
362
+ 'allowedFileOperations',
363
+ 'excludedPaths'
364
+ ].forEach(key => {
365
+ if (newConfig[key]) {
366
+ const existingItems = merged[key] || [];
367
+ const newItems = newConfig[key];
368
+
369
+ // 合并并去重
370
+ const combined = [...new Set([...existingItems, ...newItems])];
371
+ merged[key] = combined;
372
+ }
373
+ });
374
+
375
+ return merged;
376
+ }
377
+
378
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "claude-config-cli",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "packageManager": "pnpm@10.20.0",
6
+ "description": "CLI tool to manage Claude Code configuration files",
7
+ "author": "KazooTTT <work@kazoottt.top>",
8
+ "license": "MIT",
9
+ "homepage": "https://github.com/KazooTTT/claude-config-generate#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/KazooTTT/claude-config-generate.git"
13
+ },
14
+ "bugs": "https://github.com/KazooTTT/claude-config-generate/issues",
15
+ "keywords": [
16
+ "claude",
17
+ "config",
18
+ "cli",
19
+ "claude-code",
20
+ "configuration"
21
+ ],
22
+ "bin": {
23
+ "claude-config": "./cli.js"
24
+ },
25
+ "files": [
26
+ "cli.js"
27
+ ],
28
+ "scripts": {
29
+ "test": "node cli.js --help",
30
+ "release": "bumpp",
31
+ "release:next": "bumpp next --yes",
32
+ "release:patch": "bumpp patch --yes",
33
+ "release:minor": "bumpp minor --yes",
34
+ "release:major": "bumpp major --yes",
35
+ "prepack": "npm run test"
36
+ },
37
+ "dependencies": {
38
+ "chalk": "^5.3.0",
39
+ "commander": "^12.0.0",
40
+ "inquirer": "^9.2.0"
41
+ },
42
+ "devDependencies": {
43
+ "bumpp": "^10.3.2"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
48
+ }