claude-code-model-switch 2.2.2 → 2.2.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.
@@ -0,0 +1,96 @@
1
+ name: Release npm package
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ version:
7
+ description: "Version to publish, for example 2.2.4"
8
+ required: true
9
+ type: string
10
+
11
+ permissions:
12
+ contents: write
13
+ id-token: write
14
+
15
+ concurrency:
16
+ group: npm-release
17
+ cancel-in-progress: false
18
+
19
+ jobs:
20
+ release:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - name: Check out source
24
+ uses: actions/checkout@v6
25
+ with:
26
+ fetch-depth: 0
27
+
28
+ - name: Set up Node.js
29
+ uses: actions/setup-node@v7
30
+ with:
31
+ node-version: 24
32
+ registry-url: https://registry.npmjs.org
33
+ package-manager-cache: false
34
+
35
+ - name: Validate release version
36
+ env:
37
+ RELEASE_VERSION: ${{ inputs.version }}
38
+ run: |
39
+ if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
40
+ echo "Invalid semantic version: $RELEASE_VERSION"
41
+ exit 1
42
+ fi
43
+
44
+ PACKAGE_NAME="$(node -p "require('./package.json').name")"
45
+ if git ls-remote --exit-code --tags origin "refs/tags/v$RELEASE_VERSION" >/dev/null 2>&1; then
46
+ echo "Release tag v$RELEASE_VERSION already exists"
47
+ exit 1
48
+ fi
49
+
50
+ if npm view "$PACKAGE_NAME@$RELEASE_VERSION" version --registry=https://registry.npmjs.org >/dev/null 2>&1; then
51
+ echo "npm version $RELEASE_VERSION is already published"
52
+ exit 1
53
+ fi
54
+
55
+ CURRENT_VERSION="$(node -p "require('./package.json').version")"
56
+ if [[ "$CURRENT_VERSION" != "$RELEASE_VERSION" ]]; then
57
+ npm version "$RELEASE_VERSION" --no-git-tag-version --dry-run
58
+ else
59
+ echo "Package is already at version $RELEASE_VERSION"
60
+ fi
61
+
62
+ - name: Install dependencies
63
+ run: npm ci
64
+
65
+ - name: Run tests
66
+ run: npm test
67
+
68
+ - name: Update package version
69
+ env:
70
+ RELEASE_VERSION: ${{ inputs.version }}
71
+ run: |
72
+ CURRENT_VERSION="$(node -p "require('./package.json').version")"
73
+ if [[ "$CURRENT_VERSION" != "$RELEASE_VERSION" ]]; then
74
+ npm version "$RELEASE_VERSION" --no-git-tag-version
75
+ else
76
+ echo "Package is already at version $RELEASE_VERSION"
77
+ fi
78
+
79
+ - name: Publish package
80
+ run: npm publish --access public
81
+
82
+ - name: Commit and push release
83
+ env:
84
+ RELEASE_VERSION: ${{ inputs.version }}
85
+ BRANCH_NAME: ${{ github.ref_name }}
86
+ run: |
87
+ git config user.name "github-actions[bot]"
88
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
89
+ if ! git diff --quiet -- package.json package-lock.json; then
90
+ git add package.json package-lock.json
91
+ git commit -m "chore(release): $RELEASE_VERSION"
92
+ else
93
+ echo "Package version files are already up to date"
94
+ fi
95
+ git tag "v$RELEASE_VERSION"
96
+ git push origin "HEAD:$BRANCH_NAME" "v$RELEASE_VERSION"
package/README.md CHANGED
@@ -95,7 +95,8 @@ Edit the configuration file `~/.claude-code-model-switch/settings.json`:
95
95
  | `ANTHROPIC_DEFAULT_SONNET_MODEL` | ❌ | Same as `ANTHROPIC_MODEL` | Default Sonnet model |
96
96
  | `ANTHROPIC_DEFAULT_OPUS_MODEL` | ❌ | Same as `ANTHROPIC_MODEL` | Default Opus model |
97
97
  | `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | ❌ | `8192` | Maximum output tokens |
98
- | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | ❌ | `1` | Disable non-essential traffic |
98
+ | `CLAUDE_CODE_ATTRIBUTION_HEADER` | ❌ | `0` | Attribution header setting |
99
+ | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | ❌ | Not set | Disable non-essential traffic |
99
100
 
100
101
  ### Special Rules
101
102
 
@@ -104,7 +105,8 @@ Edit the configuration file `~/.claude-code-model-switch/settings.json`:
104
105
  3. **shared env vars**: variables in `shared` apply to every model; model-specific values override shared ones
105
106
  4. **Default Values**:
106
107
  - `CLAUDE_CODE_MAX_OUTPUT_TOKENS` defaults to `8192`
107
- - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` defaults to `1`
108
+ - `CLAUDE_CODE_ATTRIBUTION_HEADER` defaults to `0`
109
+ - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is only set when explicitly configured
108
110
  - Unset model fields automatically use the value of `ANTHROPIC_MODEL`
109
111
  4. **Configuration File**: Empty configuration file `{"shared": {}, "models": {}}` is automatically created on first run
110
112
  5. **Unset Command**: `ccms unset` removes only model-specific variables from `settings.json` and restores official models
@@ -138,4 +140,4 @@ Issues and Pull Requests are welcome!
138
140
 
139
141
  ## 🔗 Repository
140
142
 
141
- https://github.com/musnows/claude-code-model-switch
143
+ https://github.com/musnows/claude-code-model-switch
package/README_CN.md CHANGED
@@ -95,7 +95,8 @@ ccms shared unset API_TIMEOUT_MS
95
95
  | `ANTHROPIC_DEFAULT_SONNET_MODEL` | ❌ | 同 `ANTHROPIC_MODEL` | 默认 Sonnet 模型 |
96
96
  | `ANTHROPIC_DEFAULT_OPUS_MODEL` | ❌ | 同 `ANTHROPIC_MODEL` | 默认 Opus 模型 |
97
97
  | `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | ❌ | `8192` | 最大输出令牌数 |
98
- | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | ❌ | `1` | 禁用非必要流量 |
98
+ | `CLAUDE_CODE_ATTRIBUTION_HEADER` | ❌ | `0` | 归属标识头设置 |
99
+ | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | ❌ | 不设置 | 禁用非必要流量 |
99
100
 
100
101
  ### 特殊规则
101
102
 
@@ -104,7 +105,8 @@ ccms shared unset API_TIMEOUT_MS
104
105
  3. **shared 公共变量**:`shared` 中的环境变量会应用到所有模型;单个模型里同名变量会覆盖 shared
105
106
  4. **默认值处理**:
106
107
  - `CLAUDE_CODE_MAX_OUTPUT_TOKENS` 默认为 `8192`
107
- - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` 默认为 `1`
108
+ - `CLAUDE_CODE_ATTRIBUTION_HEADER` 默认为 `0`
109
+ - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` 仅在显式配置时设置
108
110
  - 未设置的模型字段会自动使用 `ANTHROPIC_MODEL` 的值
109
111
  4. **配置文件**:首次运行时会自动创建空的配置文件 `{"shared": {}, "models": {}}`
110
112
  5. **Unset 命令**:`ccms unset` 只删除 `settings.json` 中的模型相关变量,恢复使用官方模型
@@ -138,4 +140,4 @@ MIT License
138
140
 
139
141
  ## 🔗 开源地址
140
142
 
141
- https://github.com/musnows/claude-code-model-switch
143
+ https://github.com/musnows/claude-code-model-switch
package/argv.js ADDED
@@ -0,0 +1,33 @@
1
+ function findStartModelIndex(args) {
2
+ for (let index = 0; index < args.length - 1; index += 1) {
3
+ if (args[index] === 'start' && args[index + 1] && !args[index + 1].startsWith('-')) {
4
+ return index;
5
+ }
6
+ }
7
+
8
+ return -1;
9
+ }
10
+
11
+ function normalizeStartCommandArgs(args) {
12
+ if (args[0] === 'start') {
13
+ return args;
14
+ }
15
+
16
+ const startIndex = findStartModelIndex(args);
17
+ if (startIndex === -1) {
18
+ return args;
19
+ }
20
+
21
+ const modelName = args[startIndex + 1];
22
+ return [
23
+ 'start',
24
+ modelName,
25
+ ...args.slice(0, startIndex),
26
+ ...args.slice(startIndex + 2),
27
+ ];
28
+ }
29
+
30
+ module.exports = {
31
+ findStartModelIndex,
32
+ normalizeStartCommandArgs,
33
+ };
package/index.js CHANGED
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { spawn } = require('child_process');
6
6
  const { program } = require('commander');
7
+ const { normalizeStartCommandArgs } = require('./argv');
7
8
 
8
9
  const CLAUDE_SETTINGS_PATH = path.join(process.env.HOME, '.claude', 'settings.json');
9
10
  const MODEL_CONFIG_PATH = path.join(process.env.HOME, '.claude-code-model-switch', 'settings.json');
@@ -11,7 +12,6 @@ const CLAUDE_BIN = process.env.CLAUDE_BIN || 'claude';
11
12
  const YOLO_ARGS = ['--dangerously-skip-permissions'];
12
13
 
13
14
  const DEFAULT_MAX_OUTPUT_TOKENS = '8192';
14
- const DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC = '1';
15
15
  const DEFAULT_ATTRIBUTION_HEADER = '0';
16
16
  const DEFAULT_MAX_CONTEXT_TOKENS = '200000';
17
17
  const DEFAULT_AUTOCOMPACT_PCT_OVERRIDE = '80';
@@ -225,10 +225,6 @@ function buildModelEnv(modelConfig) {
225
225
  env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = DEFAULT_MAX_OUTPUT_TOKENS;
226
226
  }
227
227
 
228
- if (!env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
229
- env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC;
230
- }
231
-
232
228
  return env;
233
229
  }
234
230
 
@@ -327,10 +323,6 @@ function applyModelConfig(modelConfig) {
327
323
  settings.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = DEFAULT_MAX_OUTPUT_TOKENS;
328
324
  }
329
325
 
330
- if (!settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
331
- settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC;
332
- }
333
-
334
326
  saveClaudeSettings(settings);
335
327
  console.log(`Switched to model: ${settings.env.ANTHROPIC_MODEL}`);
336
328
  }
@@ -428,7 +420,7 @@ function listModels() {
428
420
  program
429
421
  .name('ccms')
430
422
  .description('Claude Code Model Switch - Manage and launch Claude Code with configured models')
431
- .version('2.2.2');
423
+ .version(require('./package.json').version);
432
424
 
433
425
  program
434
426
  .command('start <model> [claude-args...]')
@@ -503,6 +495,14 @@ program
503
495
  process.exit(1);
504
496
  });
505
497
 
498
+ // Multica may place Claude protocol args before the ccms subcommand. Normalize
499
+ // only when a concrete start/model pair exists; otherwise leave argv untouched
500
+ // so Commander keeps its normal unsupported-argument error.
501
+ process.argv = [
502
+ ...process.argv.slice(0, 2),
503
+ ...normalizeStartCommandArgs(process.argv.slice(2)),
504
+ ];
505
+
506
506
  program.parse();
507
507
 
508
508
  if (!process.argv.slice(2).length) {
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "claude-code-model-switch",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "description": "Launch Claude Code with preconfigured model environments",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/musnows/claude-code-model-switch"
8
+ },
5
9
  "main": "index.js",
6
10
  "bin": {
7
11
  "ccms": "index.js"
8
12
  },
9
13
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
14
+ "test": "node --test"
11
15
  },
12
16
  "keywords": [
13
17
  "claude",
@@ -0,0 +1,34 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+
4
+ const { findStartModelIndex, normalizeStartCommandArgs } = require('../argv');
5
+
6
+ test('finds start and model after protocol arguments', () => {
7
+ const args = ['-p', '--output-format', 'stream-json', 'start', 'dsf'];
8
+
9
+ assert.equal(findStartModelIndex(args), 3);
10
+ });
11
+
12
+ test('moves start and model before protocol arguments', () => {
13
+ const args = ['-p', '--output-format', 'stream-json', 'start', 'dsf'];
14
+
15
+ assert.deepEqual(normalizeStartCommandArgs(args), [
16
+ 'start',
17
+ 'dsf',
18
+ '-p',
19
+ '--output-format',
20
+ 'stream-json',
21
+ ]);
22
+ });
23
+
24
+ test('preserves the standard start command order', () => {
25
+ const args = ['start', 'glm53flash', '-p', 'hello'];
26
+
27
+ assert.deepEqual(normalizeStartCommandArgs(args), args);
28
+ });
29
+
30
+ test('leaves arguments unchanged when no start model pair exists', () => {
31
+ const args = ['-p', 'hello'];
32
+
33
+ assert.deepEqual(normalizeStartCommandArgs(args), args);
34
+ });
@@ -0,0 +1,12 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const test = require('node:test');
4
+ const assert = require('node:assert/strict');
5
+
6
+ const source = fs.readFileSync(path.join(__dirname, '..', 'index.js'), 'utf8');
7
+
8
+ test('does not assign a default non-essential traffic setting', () => {
9
+ assert.doesNotMatch(source, /DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC/);
10
+ assert.doesNotMatch(source, /env\.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\s*=/);
11
+ assert.doesNotMatch(source, /settings\.env\.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\s*=/);
12
+ });