@kaitranntt/ccs 7.73.1-dev.2 → 7.73.1-dev.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.
package/README.md CHANGED
@@ -177,6 +177,9 @@ ccs ollama "summarize these logs"
177
177
  ## Contribute And Report Safely
178
178
 
179
179
  - Contributing guide: [CONTRIBUTING.md](./CONTRIBUTING.md)
180
+ - Daily local gate: `bun run format && bun run lint:fix && bun run validate` (`validate` is the fast path only)
181
+ - Before review or merge confidence: `bun run validate:ci-parity`
182
+ - If PR checks stay queued for more than 10 minutes, assume the self-hosted runner is offline and notify a maintainer instead of retrying blindly
180
183
  - Starter work:
181
184
  [good first issue](https://github.com/kaitranntt/ccs/labels/good%20first%20issue),
182
185
  [help wanted](https://github.com/kaitranntt/ccs/labels/help%20wanted)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaitranntt/ccs",
3
- "version": "7.73.1-dev.2",
3
+ "version": "7.73.1-dev.3",
4
4
  "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more",
5
5
  "keywords": [
6
6
  "cli",
@@ -70,12 +70,14 @@
70
70
  "lint:fix": "eslint src/ --fix",
71
71
  "format": "prettier --write src/",
72
72
  "format:check": "prettier --check src/",
73
- "validate": "bun run typecheck && bun run lint:fix && bun run format:check && bun run test:all",
73
+ "validate": "bun run typecheck && bun run lint && bun run format:check && bun run test:fast",
74
74
  "validate:ci-parity": "bash scripts/ci-parity-gate.sh",
75
75
  "verify:bundle": "node scripts/verify-bundle.js",
76
76
  "test": "bun run build && bun run test:all",
77
77
  "test:ci": "bun run test:all",
78
- "test:all": "bun test --max-concurrency=1 tests/unit tests/integration tests/npm",
78
+ "test:fast": "node scripts/run-test-bucket.js fast",
79
+ "test:slow": "node scripts/run-test-bucket.js slow",
80
+ "test:all": "node scripts/run-test-bucket.js all",
79
81
  "test:unit": "bun test tests/unit",
80
82
  "test:npm": "bun test tests/npm/",
81
83
  "test:native": "bash tests/native/unix/edge-cases.sh",
@@ -55,8 +55,14 @@ if git show-ref --verify --quiet "refs/remotes/origin/$BASE_BRANCH"; then
55
55
  fi
56
56
  fi
57
57
 
58
- echo "[i] Running CI-equivalent local checks..."
58
+ echo "[i] Running CI-parity local checks..."
59
+ # `set -euo pipefail` above makes every step fail fast. Keep these commands
60
+ # explicit so parity drift is visible when CI changes.
61
+ bun run typecheck
62
+ bun run lint
63
+ bun run format:check
59
64
  bun run build:all
60
- bun run validate
65
+ bun run test:all
66
+ CCS_E2E_SKIP_BUILD=1 bun run test:e2e
61
67
 
62
68
  echo "[OK] CI parity gate passed."
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { spawnSync } = require('node:child_process');
6
+
7
+ const rootDir = path.resolve(__dirname, '..');
8
+ const candidateRoots = ['tests/unit', 'tests/integration', 'tests/npm'];
9
+ // Add a `.ts` test to `slowTests` when ANY of these apply:
10
+ // 1. It spawns a child process (CLI, bun test, node, gh, etc.).
11
+ // 2. It binds a port, starts a server, or talks to localhost.
12
+ // 3. It reads a real file from `dist/` or the repo root at runtime.
13
+ // 4. It waits on a timer > 500ms or a filesystem watcher.
14
+ // 5. A single run consistently takes > 1500ms on reference hardware.
15
+ // Tests that literally reference `dist/` in source are auto-forced slow by
16
+ // `readsBuiltDist`. This list is the manual catch-all for `.ts` tests that
17
+ // meet the criteria above without the literal `dist/` string.
18
+ // `tests/unit/scripts/run-test-bucket.test.js` verifies every path here exists
19
+ // (catches deletion drift) but CANNOT detect new undeclared slow tests.
20
+ // Automated perf-budget enforcement tracked in issue #1071.
21
+ const slowTests = [
22
+ 'tests/integration/cursor-daemon-lifecycle.test.ts',
23
+ 'tests/integration/proxy/daemon-lifecycle.test.ts',
24
+ 'tests/unit/commands/persist-command-handler.test.ts',
25
+ 'tests/unit/hooks/ccs-browser-mcp-server.test.ts',
26
+ 'tests/unit/targets/codex-runtime-integration.test.ts',
27
+ 'tests/unit/targets/codex-settings-bridge-launch.test.ts',
28
+ 'tests/unit/targets/droid-command-routing-integration.test.ts',
29
+ 'tests/unit/targets/droid-config-manager.test.ts',
30
+ 'tests/unit/targets/settings-profile-browser-launch.test.ts',
31
+ 'tests/unit/targets/settings-profile-image-analysis-launch.test.ts',
32
+ 'tests/unit/targets/settings-profile-websearch-launch.test.ts',
33
+ 'tests/unit/web-server/cursor-routes.test.ts',
34
+ 'tests/unit/web-server/websearch-routes.test.ts',
35
+ ];
36
+ // CommonJS-heavy JS suites stay slow by default because many of them mutate
37
+ // module cache or process state. Opt them into `test:fast` only after they are
38
+ // proven stable in the mixed fast bucket.
39
+ const fastJsTests = new Set([
40
+ 'tests/unit/flag-parsing-simple.test.js',
41
+ ]);
42
+
43
+ const filePattern = /(\.test\.(c|m)?[jt]s|\.spec\.(c|m)?[jt]s|-test\.(c|m)?[jt]s)$/;
44
+
45
+ function collectFiles(dir, files = []) {
46
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
47
+ const fullPath = path.join(dir, entry.name);
48
+ if (entry.isDirectory()) {
49
+ collectFiles(fullPath, files);
50
+ continue;
51
+ }
52
+
53
+ if (filePattern.test(entry.name)) {
54
+ files.push(path.relative(rootDir, fullPath).split(path.sep).join('/'));
55
+ }
56
+ }
57
+
58
+ return files;
59
+ }
60
+
61
+ function readsBuiltDist(relativePath) {
62
+ const source = fs.readFileSync(path.join(rootDir, relativePath), 'utf8');
63
+ return source.includes('dist/');
64
+ }
65
+
66
+ function getDiscoveredTests() {
67
+ return candidateRoots
68
+ .flatMap((relativeDir) => collectFiles(path.join(rootDir, relativeDir)))
69
+ .sort();
70
+ }
71
+
72
+ function shouldForceSlow(file) {
73
+ if (file.startsWith('tests/npm/')) {
74
+ return true;
75
+ }
76
+
77
+ if (/\.(c|m)?js$/.test(file) && !fastJsTests.has(file)) {
78
+ return true;
79
+ }
80
+
81
+ return readsBuiltDist(file);
82
+ }
83
+
84
+ function getSlowSet() {
85
+ const discovered = getDiscoveredTests();
86
+ const forceSlow = discovered.filter((file) => shouldForceSlow(file));
87
+ return new Set([...slowTests, ...forceSlow]);
88
+ }
89
+
90
+ function selectBucket(name) {
91
+ const discovered = getDiscoveredTests();
92
+ const slowSet = getSlowSet();
93
+
94
+ return name === 'slow'
95
+ ? [...slowSet].sort()
96
+ : discovered.filter((file) => !slowSet.has(file));
97
+ }
98
+
99
+ function ensureBuildForSlowBucket() {
100
+ if (fs.existsSync(path.join(rootDir, 'dist', 'ccs.js'))) {
101
+ return 0;
102
+ }
103
+
104
+ const build = spawnSync('bun', ['run', 'build'], {
105
+ cwd: rootDir,
106
+ stdio: 'inherit',
107
+ shell: process.platform === 'win32',
108
+ });
109
+
110
+ return build.status ?? 1;
111
+ }
112
+
113
+ function runBucket(name) {
114
+ const selected = selectBucket(name);
115
+
116
+ if (selected.length === 0) {
117
+ console.error(`[X] No tests matched the '${name}' bucket.`);
118
+ return 1;
119
+ }
120
+
121
+ if (name === 'slow') {
122
+ const buildStatus = ensureBuildForSlowBucket();
123
+ if (buildStatus !== 0) {
124
+ return buildStatus;
125
+ }
126
+ }
127
+
128
+ // Slow bucket forces sequential execution because it spawns subprocesses,
129
+ // binds ports, and touches shared state — parallelism causes flakes.
130
+ // Fast bucket keeps bun's default parallelism for speed.
131
+ const bunArgs = name === 'slow'
132
+ ? ['test', '--max-concurrency=1', ...selected]
133
+ : ['test', ...selected];
134
+
135
+ const result = spawnSync('bun', bunArgs, {
136
+ cwd: rootDir,
137
+ stdio: 'inherit',
138
+ shell: process.platform === 'win32',
139
+ });
140
+
141
+ return result.status ?? 1;
142
+ }
143
+
144
+ function main(args = process.argv.slice(2)) {
145
+ const bucket = args[0];
146
+
147
+ if (!['fast', 'slow', 'all'].includes(bucket)) {
148
+ console.error('[X] Usage: node scripts/run-test-bucket.js <fast|slow|all>');
149
+ return 1;
150
+ }
151
+
152
+ if (bucket === 'all') {
153
+ let exitCode = 0;
154
+
155
+ for (const name of ['fast', 'slow']) {
156
+ const status = runBucket(name);
157
+ if (status !== 0) {
158
+ exitCode = status;
159
+ }
160
+ }
161
+
162
+ return exitCode;
163
+ }
164
+
165
+ return runBucket(bucket);
166
+ }
167
+
168
+ if (require.main === module) {
169
+ process.exit(main());
170
+ }
171
+
172
+ module.exports = {
173
+ slowTests,
174
+ fastJsTests,
175
+ readsBuiltDist,
176
+ shouldForceSlow,
177
+ getDiscoveredTests,
178
+ getSlowSet,
179
+ selectBucket,
180
+ main,
181
+ };