@tkpdx01/ccc 1.3.4 → 1.3.5

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 CHANGED
@@ -60,7 +60,7 @@ ccc webdav status # View sync status / 查看同步状态
60
60
  - **Template Support / 模板**: Based on `~/.claude/settings.json`
61
61
  - **Smart Import / 智能导入**: Auto-detect API URL and token
62
62
  - **Sync Settings / 同步**: Update from template, preserve credentials
63
- - **Claude Env Defaults / Claude 环境变量默认值**: Auto-ensure `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`, `CLAUDE_CODE_ATTRIBUTION_HEADER=0`, and `DISABLE_INSTALLATION_CHECKS=1` in both template and profiles
63
+ - **Claude Env Defaults / Claude 环境变量默认值**: Auto-ensure these values in the `env` section of both `~/.claude/settings.json` and each profile: `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`, `CLAUDE_CODE_ATTRIBUTION_HEADER=0`, `DISABLE_INSTALLATION_CHECKS=1`
64
64
  - **WebDAV Cloud Sync / 云同步**: Encrypted sync across devices
65
65
 
66
66
  ## Sync Command / 同步命令
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tkpdx01/ccc",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "Claude Code Settings Launcher - Manage multiple Claude Code profiles",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -13,7 +13,8 @@ import {
13
13
  resolveProfile,
14
14
  getProfileCredentials,
15
15
  getClaudeSettingsTemplate,
16
- ensureRequiredClaudeEnvSettings
16
+ ensureRequiredClaudeEnvSettings,
17
+ ensureClaudeSettingsExtras
17
18
  } from '../profiles.js';
18
19
 
19
20
  export function editCommand(program) {
@@ -98,6 +99,7 @@ export function editCommand(program) {
98
99
 
99
100
  // 确保主配置(~/.claude/settings.json)与 profile 都包含必要 env 设置
100
101
  ensureRequiredClaudeEnvSettings();
102
+ ensureClaudeSettingsExtras();
101
103
  currentProfile.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
102
104
  currentProfile.env.CLAUDE_CODE_ATTRIBUTION_HEADER = '0';
103
105
  currentProfile.env.DISABLE_INSTALLATION_CHECKS = '1';
@@ -5,10 +5,42 @@ import {
5
5
  getProfiles,
6
6
  profileExists,
7
7
  createProfileFromTemplate,
8
- setDefaultProfile
8
+ setDefaultProfile,
9
+ ensureClaudeSettingsExtras
9
10
  } from '../profiles.js';
10
11
  import { launchClaude } from '../launch.js';
11
12
 
13
+ const RESERVED_PROFILE_NAMES = [
14
+ 'list',
15
+ 'ls',
16
+ 'use',
17
+ 'show',
18
+ 'import',
19
+ 'if',
20
+ 'new',
21
+ 'edit',
22
+ 'delete',
23
+ 'rm',
24
+ 'sync',
25
+ 'webdav',
26
+ 'help'
27
+ ];
28
+
29
+ function isReservedProfileName(name) {
30
+ return RESERVED_PROFILE_NAMES.includes(name);
31
+ }
32
+
33
+ function validateProfileName(input) {
34
+ const trimmed = input.trim();
35
+ if (!trimmed) {
36
+ return '请输入配置名称';
37
+ }
38
+ if (isReservedProfileName(trimmed)) {
39
+ return '配置名称不能使用命令关键词';
40
+ }
41
+ return true;
42
+ }
43
+
12
44
  export function newCommand(program) {
13
45
  program
14
46
  .command('new [name]')
@@ -21,10 +53,16 @@ export function newCommand(program) {
21
53
  type: 'input',
22
54
  name: 'profileName',
23
55
  message: '配置名称:',
24
- validate: (input) => input.trim() ? true : '请输入配置名称'
56
+ validate: validateProfileName
25
57
  }
26
58
  ]);
27
59
  name = profileName;
60
+ } else {
61
+ const validationResult = validateProfileName(name);
62
+ if (validationResult !== true) {
63
+ console.log(chalk.red(validationResult));
64
+ process.exit(1);
65
+ }
28
66
  }
29
67
 
30
68
  // 检查是否已存在
@@ -60,7 +98,8 @@ export function newCommand(program) {
60
98
  type: 'input',
61
99
  name: 'finalName',
62
100
  message: 'Profile 名称:',
63
- default: name
101
+ default: name,
102
+ validate: validateProfileName
64
103
  }
65
104
  ]);
66
105
 
@@ -81,6 +120,7 @@ export function newCommand(program) {
81
120
  }
82
121
 
83
122
  ensureDirs();
123
+ ensureClaudeSettingsExtras();
84
124
  createProfileFromTemplate(finalName, apiUrl, apiKey);
85
125
  console.log(chalk.green(`\n✓ 配置 "${finalName}" 已创建(基于 ~/.claude/settings.json)`));
86
126
 
package/src/profiles.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import os from 'os';
3
4
  import { CONFIG_DIR, PROFILES_DIR, DEFAULT_FILE, CLAUDE_SETTINGS_PATH } from './config.js';
4
5
 
5
6
  function stringifyClaudeSettings(settings) {
@@ -138,6 +139,67 @@ export function ensureRequiredClaudeEnvSettings() {
138
139
  });
139
140
  }
140
141
 
142
+ // 获取 statusLine 的 ccline 路径(适配不同操作系统)
143
+ function getCclineCommand() {
144
+ const platform = os.platform();
145
+ if (platform === 'win32') {
146
+ return '%USERPROFILE%\\.claude\\ccline\\ccline.exe';
147
+ } else {
148
+ // Linux 和 macOS
149
+ return '~/.claude/ccline/ccline';
150
+ }
151
+ }
152
+
153
+ // 确保主配置包含 attribution/includeCoAuthoredBy 和 statusLine 设置
154
+ export function ensureClaudeSettingsExtras() {
155
+ const template = getClaudeSettingsTemplate();
156
+ if (!template) {
157
+ return null;
158
+ }
159
+
160
+ let changed = false;
161
+
162
+ // 确保 attribution 禁用(commit/pr 为空字符串)
163
+ if (!template.attribution || typeof template.attribution !== 'object' || Array.isArray(template.attribution)) {
164
+ template.attribution = { commit: '', pr: '' };
165
+ changed = true;
166
+ } else {
167
+ if (template.attribution.commit !== '' || template.attribution.pr !== '') {
168
+ template.attribution.commit = '';
169
+ template.attribution.pr = '';
170
+ changed = true;
171
+ }
172
+ }
173
+
174
+ // 兼容旧版本:确保 includeCoAuthoredBy: false
175
+ if (template.includeCoAuthoredBy !== false) {
176
+ template.includeCoAuthoredBy = false;
177
+ changed = true;
178
+ }
179
+
180
+ // 确保 statusLine 配置
181
+ const expectedCommand = getCclineCommand();
182
+ const expectedStatusLine = {
183
+ type: 'command',
184
+ command: expectedCommand,
185
+ padding: 0
186
+ };
187
+
188
+ if (!template.statusLine ||
189
+ template.statusLine.type !== 'command' ||
190
+ template.statusLine.command !== expectedCommand ||
191
+ template.statusLine.padding !== 0) {
192
+ template.statusLine = expectedStatusLine;
193
+ changed = true;
194
+ }
195
+
196
+ if (changed) {
197
+ fs.writeFileSync(CLAUDE_SETTINGS_PATH, stringifyClaudeSettings(template));
198
+ }
199
+
200
+ return template;
201
+ }
202
+
141
203
  // 读取 profile 配置
142
204
  export function readProfile(name) {
143
205
  const profilePath = getProfilePath(name);