@tkpdx01/ccc 1.3.4 → 1.3.6

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.6",
4
4
  "description": "Claude Code Settings Launcher - Manage multiple Claude Code profiles",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -13,7 +13,9 @@ import {
13
13
  resolveProfile,
14
14
  getProfileCredentials,
15
15
  getClaudeSettingsTemplate,
16
- ensureRequiredClaudeEnvSettings
16
+ ensureRequiredClaudeEnvSettings,
17
+ ensureClaudeSettingsExtras,
18
+ applyClaudeSettingsExtras
17
19
  } from '../profiles.js';
18
20
 
19
21
  export function editCommand(program) {
@@ -98,9 +100,11 @@ export function editCommand(program) {
98
100
 
99
101
  // 确保主配置(~/.claude/settings.json)与 profile 都包含必要 env 设置
100
102
  ensureRequiredClaudeEnvSettings();
103
+ ensureClaudeSettingsExtras();
101
104
  currentProfile.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
102
105
  currentProfile.env.CLAUDE_CODE_ATTRIBUTION_HEADER = '0';
103
106
  currentProfile.env.DISABLE_INSTALLATION_CHECKS = '1';
107
+ applyClaudeSettingsExtras(currentProfile);
104
108
 
105
109
  // 如果重命名
106
110
  if (newName && newName !== profile) {
@@ -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,77 @@ 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
+ export function applyClaudeSettingsExtras(target) {
154
+ if (!target || typeof target !== 'object') {
155
+ return false;
156
+ }
157
+
158
+ let changed = false;
159
+
160
+ // 确保 attribution 禁用(commit/pr 为空字符串)
161
+ if (!target.attribution || typeof target.attribution !== 'object' || Array.isArray(target.attribution)) {
162
+ target.attribution = { commit: '', pr: '' };
163
+ changed = true;
164
+ } else {
165
+ if (target.attribution.commit !== '' || target.attribution.pr !== '') {
166
+ target.attribution.commit = '';
167
+ target.attribution.pr = '';
168
+ changed = true;
169
+ }
170
+ }
171
+
172
+ // 兼容旧版本:确保 includeCoAuthoredBy: false
173
+ if (target.includeCoAuthoredBy !== false) {
174
+ target.includeCoAuthoredBy = false;
175
+ changed = true;
176
+ }
177
+
178
+ // 确保 statusLine 配置
179
+ const expectedCommand = getCclineCommand();
180
+ const expectedStatusLine = {
181
+ type: 'command',
182
+ command: expectedCommand,
183
+ padding: 0
184
+ };
185
+
186
+ if (!target.statusLine ||
187
+ target.statusLine.type !== 'command' ||
188
+ target.statusLine.command !== expectedCommand ||
189
+ target.statusLine.padding !== 0) {
190
+ target.statusLine = expectedStatusLine;
191
+ changed = true;
192
+ }
193
+
194
+ return changed;
195
+ }
196
+
197
+ // 确保主配置包含 attribution/includeCoAuthoredBy 和 statusLine 设置
198
+ export function ensureClaudeSettingsExtras() {
199
+ const template = getClaudeSettingsTemplate();
200
+ if (!template) {
201
+ return null;
202
+ }
203
+
204
+ const changed = applyClaudeSettingsExtras(template);
205
+
206
+ if (changed) {
207
+ fs.writeFileSync(CLAUDE_SETTINGS_PATH, stringifyClaudeSettings(template));
208
+ }
209
+
210
+ return template;
211
+ }
212
+
141
213
  // 读取 profile 配置
142
214
  export function readProfile(name) {
143
215
  const profilePath = getProfilePath(name);
@@ -163,6 +235,7 @@ export function createProfileFromTemplate(name, apiUrl, apiKey) {
163
235
  // 先确保主配置包含必要 env 设置(也会写回 ~/.claude/settings.json)
164
236
  const ensuredTemplate = ensureRequiredClaudeEnvSettings();
165
237
  const template = ensuredTemplate || getClaudeSettingsTemplate() || {};
238
+ applyClaudeSettingsExtras(template);
166
239
 
167
240
  // 确保 env 对象存在
168
241
  if (!template.env) {
@@ -189,6 +262,7 @@ export function syncProfileWithTemplate(name) {
189
262
  if (!template) {
190
263
  return null;
191
264
  }
265
+ applyClaudeSettingsExtras(template);
192
266
 
193
267
  const currentProfile = readProfile(name);
194
268
  if (!currentProfile) {
@@ -210,6 +284,7 @@ export function syncProfileWithTemplate(name) {
210
284
  newProfile.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
211
285
  newProfile.env.CLAUDE_CODE_ATTRIBUTION_HEADER = '0';
212
286
  newProfile.env.DISABLE_INSTALLATION_CHECKS = '1';
287
+ applyClaudeSettingsExtras(newProfile);
213
288
 
214
289
  saveProfile(name, newProfile);
215
290
  return newProfile;