@kaitranntt/ccs 6.5.0-dev.7 → 6.6.0-dev.2
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/package.json +6 -3
- package/scripts/send-discord-release.cjs +214 -0
- package/VERSION +0 -1
- package/scripts/sync-version-plugin.cjs +0 -41
- package/scripts/sync-version.js +0 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaitranntt/ccs",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.6.0-dev.2",
|
|
4
4
|
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -75,8 +75,6 @@
|
|
|
75
75
|
"ui:build": "cd ui && bun run build",
|
|
76
76
|
"ui:preview": "cd ui && bun run preview",
|
|
77
77
|
"ui:validate": "cd ui && bun run validate",
|
|
78
|
-
"prepublishOnly": "node scripts/sync-version.js",
|
|
79
|
-
"prepack": "node scripts/sync-version.js",
|
|
80
78
|
"prepare": "husky",
|
|
81
79
|
"postinstall": "node scripts/postinstall.js"
|
|
82
80
|
},
|
|
@@ -98,7 +96,11 @@
|
|
|
98
96
|
"@commitlint/cli": "^20.1.0",
|
|
99
97
|
"@commitlint/config-conventional": "^20.0.0",
|
|
100
98
|
"@semantic-release/changelog": "^6.0.3",
|
|
99
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
101
100
|
"@semantic-release/git": "^10.0.1",
|
|
101
|
+
"@semantic-release/github": "^12.0.2",
|
|
102
|
+
"@semantic-release/npm": "^13.1.3",
|
|
103
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
102
104
|
"@tailwindcss/vite": "^4.1.17",
|
|
103
105
|
"@types/chokidar": "^2.1.7",
|
|
104
106
|
"@types/express": "^4.17.21",
|
|
@@ -108,6 +110,7 @@
|
|
|
108
110
|
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
|
109
111
|
"@typescript-eslint/parser": "^8.48.0",
|
|
110
112
|
"@vitejs/plugin-react": "^5.1.1",
|
|
113
|
+
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
111
114
|
"eslint": "^9.39.1",
|
|
112
115
|
"eslint-config-prettier": "^10.1.8",
|
|
113
116
|
"husky": "^9.1.7",
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send Release Notification to Discord using Embeds
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* node send-discord-release.cjs <type> <webhook-url>
|
|
6
|
+
*
|
|
7
|
+
* Args:
|
|
8
|
+
* type: 'production' or 'dev'
|
|
9
|
+
* webhook-url: Discord webhook URL
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const https = require('https');
|
|
14
|
+
const { URL } = require('url');
|
|
15
|
+
|
|
16
|
+
const releaseType = process.argv[2]; // 'production' or 'dev'
|
|
17
|
+
const webhookUrl = process.argv[3];
|
|
18
|
+
|
|
19
|
+
if (!releaseType || !webhookUrl) {
|
|
20
|
+
console.error('Usage: node send-discord-release.cjs <type> <webhook-url>');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Validate webhook URL is Discord
|
|
25
|
+
try {
|
|
26
|
+
const parsed = new URL(webhookUrl);
|
|
27
|
+
if (!parsed.hostname.endsWith('discord.com') || !parsed.pathname.startsWith('/api/webhooks/')) {
|
|
28
|
+
console.error('[X] Invalid Discord webhook URL');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
console.error('[X] Invalid URL format');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Extract latest release from CHANGELOG.md
|
|
38
|
+
*/
|
|
39
|
+
function extractLatestRelease() {
|
|
40
|
+
const changelogPath = 'CHANGELOG.md';
|
|
41
|
+
|
|
42
|
+
if (!fs.existsSync(changelogPath)) {
|
|
43
|
+
return {
|
|
44
|
+
version: 'Unknown',
|
|
45
|
+
date: new Date().toISOString().split('T')[0],
|
|
46
|
+
sections: {},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const content = fs.readFileSync(changelogPath, 'utf8');
|
|
51
|
+
const lines = content.split('\n');
|
|
52
|
+
|
|
53
|
+
let version = 'Unknown';
|
|
54
|
+
let date = new Date().toISOString().split('T')[0];
|
|
55
|
+
let collecting = false;
|
|
56
|
+
let currentSection = null;
|
|
57
|
+
const sections = {};
|
|
58
|
+
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
// Match: ## [1.0.0](url) (2025-01-01) or ## 1.0.0 (2025-01-01)
|
|
61
|
+
const versionMatch = line.match(/^## \[?(\d+\.\d+\.\d+(?:-dev\.\d+)?)\]?.*?\((\d{4}-\d{2}-\d{2})\)/);
|
|
62
|
+
if (versionMatch) {
|
|
63
|
+
if (!collecting) {
|
|
64
|
+
version = versionMatch[1];
|
|
65
|
+
date = versionMatch[2];
|
|
66
|
+
collecting = true;
|
|
67
|
+
continue;
|
|
68
|
+
} else {
|
|
69
|
+
break; // Found next version, stop
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!collecting) continue;
|
|
74
|
+
|
|
75
|
+
// Match section headers: ### Features, ### Bug Fixes
|
|
76
|
+
const sectionMatch = line.match(/^### (.+)/);
|
|
77
|
+
if (sectionMatch) {
|
|
78
|
+
currentSection = sectionMatch[1];
|
|
79
|
+
sections[currentSection] = [];
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Collect bullet points
|
|
84
|
+
if (currentSection && line.trim().startsWith('*')) {
|
|
85
|
+
const item = line.trim().substring(1).trim();
|
|
86
|
+
if (item) {
|
|
87
|
+
sections[currentSection].push(item);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { version, date, sections };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create Discord embed
|
|
97
|
+
*/
|
|
98
|
+
function createEmbed(release) {
|
|
99
|
+
const isDev = releaseType === 'dev';
|
|
100
|
+
const color = isDev ? 0xf59e0b : 0x10b981; // Orange for dev, Green for production
|
|
101
|
+
const title = isDev ? `Dev Release ${release.version}` : `Release ${release.version}`;
|
|
102
|
+
const url = `https://github.com/kaitranntt/ccs/releases/tag/v${release.version}`;
|
|
103
|
+
|
|
104
|
+
// Section name to indicator mapping (ASCII only per CLAUDE.md)
|
|
105
|
+
const sectionIndicators = {
|
|
106
|
+
Features: '[+]',
|
|
107
|
+
'Bug Fixes': '[X]',
|
|
108
|
+
Documentation: '[i]',
|
|
109
|
+
Styles: '[~]',
|
|
110
|
+
'Code Refactoring': '[~]',
|
|
111
|
+
'Performance Improvements': '[!]',
|
|
112
|
+
Tests: '[T]',
|
|
113
|
+
'Build System': '[B]',
|
|
114
|
+
CI: '[C]',
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const fields = [];
|
|
118
|
+
|
|
119
|
+
for (const [sectionName, items] of Object.entries(release.sections)) {
|
|
120
|
+
if (items.length === 0) continue;
|
|
121
|
+
|
|
122
|
+
const indicator = sectionIndicators[sectionName] || '[*]';
|
|
123
|
+
let fieldValue = items.map((item) => `• ${item}`).join('\n');
|
|
124
|
+
|
|
125
|
+
// Discord field value max is 1024 characters
|
|
126
|
+
if (fieldValue.length > 1024) {
|
|
127
|
+
const truncateAt = fieldValue.lastIndexOf('\n', 1000);
|
|
128
|
+
fieldValue = fieldValue.substring(0, truncateAt > 0 ? truncateAt : 1000) + '\n... *(truncated)*';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fields.push({
|
|
132
|
+
name: `${indicator} ${sectionName}`,
|
|
133
|
+
value: fieldValue,
|
|
134
|
+
inline: false,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (fields.length === 0) {
|
|
139
|
+
fields.push({
|
|
140
|
+
name: '[i] Release Notes',
|
|
141
|
+
value: 'Release completed. See changelog on GitHub.',
|
|
142
|
+
inline: false,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
title,
|
|
148
|
+
url,
|
|
149
|
+
color,
|
|
150
|
+
timestamp: new Date().toISOString(),
|
|
151
|
+
footer: {
|
|
152
|
+
text: isDev ? 'npm i @kaitranntt/ccs@dev' : 'npm i @kaitranntt/ccs@latest',
|
|
153
|
+
},
|
|
154
|
+
fields,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Send to Discord webhook
|
|
160
|
+
*/
|
|
161
|
+
function sendToDiscord(embed) {
|
|
162
|
+
const payload = {
|
|
163
|
+
username: releaseType === 'dev' ? 'CCS Dev Release' : 'CCS Release',
|
|
164
|
+
embeds: [embed],
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const url = new URL(webhookUrl);
|
|
168
|
+
const options = {
|
|
169
|
+
hostname: url.hostname,
|
|
170
|
+
path: url.pathname + url.search,
|
|
171
|
+
method: 'POST',
|
|
172
|
+
headers: {
|
|
173
|
+
'Content-Type': 'application/json',
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const req = https.request(options, (res) => {
|
|
178
|
+
let data = '';
|
|
179
|
+
|
|
180
|
+
res.on('data', (chunk) => {
|
|
181
|
+
data += chunk;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
res.on('end', () => {
|
|
185
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
186
|
+
console.log('[OK] Discord notification sent');
|
|
187
|
+
} else {
|
|
188
|
+
console.error(`[X] Discord webhook failed: ${res.statusCode}`);
|
|
189
|
+
console.error(data);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
req.on('error', (error) => {
|
|
196
|
+
console.error('[X] Error sending Discord notification:', error);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
req.write(JSON.stringify(payload));
|
|
201
|
+
req.end();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Main
|
|
205
|
+
try {
|
|
206
|
+
const release = extractLatestRelease();
|
|
207
|
+
console.log(`[i] Preparing ${releaseType} notification for v${release.version}`);
|
|
208
|
+
|
|
209
|
+
const embed = createEmbed(release);
|
|
210
|
+
sendToDiscord(embed);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error('[X] Error:', error);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
package/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
6.5.0-dev.7
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* semantic-release plugin to sync VERSION file
|
|
3
|
-
*
|
|
4
|
-
* semantic-release updates package.json but not the VERSION file.
|
|
5
|
-
* This plugin keeps VERSION in sync for shell scripts and installers.
|
|
6
|
-
*/
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
|
|
10
|
-
module.exports = {
|
|
11
|
-
/**
|
|
12
|
-
* Called during the prepare step before git commit
|
|
13
|
-
*/
|
|
14
|
-
prepare(_pluginConfig, context) {
|
|
15
|
-
const { nextRelease, logger } = context;
|
|
16
|
-
const versionFile = path.join(process.cwd(), 'VERSION');
|
|
17
|
-
|
|
18
|
-
// Write version without 'v' prefix (e.g., "5.1.0" not "v5.1.0")
|
|
19
|
-
const version = nextRelease.version;
|
|
20
|
-
fs.writeFileSync(versionFile, version + '\n');
|
|
21
|
-
logger.log('[sync-version-plugin] Updated VERSION file to %s', version);
|
|
22
|
-
|
|
23
|
-
// Also update installers for standalone installs
|
|
24
|
-
const installSh = path.join(process.cwd(), 'installers', 'install.sh');
|
|
25
|
-
const installPs1 = path.join(process.cwd(), 'installers', 'install.ps1');
|
|
26
|
-
|
|
27
|
-
if (fs.existsSync(installSh)) {
|
|
28
|
-
let content = fs.readFileSync(installSh, 'utf8');
|
|
29
|
-
content = content.replace(/^CCS_VERSION=".*"/m, `CCS_VERSION="${version}"`);
|
|
30
|
-
fs.writeFileSync(installSh, content);
|
|
31
|
-
logger.log('[sync-version-plugin] Updated installers/install.sh');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (fs.existsSync(installPs1)) {
|
|
35
|
-
let content = fs.readFileSync(installPs1, 'utf8');
|
|
36
|
-
content = content.replace(/^\$CcsVersion = ".*"/m, `$CcsVersion = "${version}"`);
|
|
37
|
-
fs.writeFileSync(installPs1, content);
|
|
38
|
-
logger.log('[sync-version-plugin] Updated installers/install.ps1');
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
};
|
package/scripts/sync-version.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
// Read VERSION file
|
|
6
|
-
const versionFile = path.join(__dirname, '..', 'VERSION');
|
|
7
|
-
const version = fs.readFileSync(versionFile, 'utf8').trim();
|
|
8
|
-
|
|
9
|
-
// Update package.json
|
|
10
|
-
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
11
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
12
|
-
pkg.version = version;
|
|
13
|
-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
14
|
-
|
|
15
|
-
console.log(`✓ Synced version ${version} to package.json`);
|