get-shit-done-cc-ui 0.2.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/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # get-shit-done-cc-ui
2
+
3
+ > GSD-UI: Visual panel for Claude Code terminal workflows
4
+
5
+ ## Installation
6
+
7
+ Run directly with npx (no installation needed):
8
+
9
+ ```bash
10
+ npx get-shit-done-cc-ui
11
+ ```
12
+
13
+ Or install globally:
14
+
15
+ ```bash
16
+ npm install -g get-shit-done-cc-ui
17
+ get-shit-done-cc-ui
18
+ ```
19
+
20
+ **First-run note:** The first time you run this command, it will download the platform-specific binary (~15MB). After the download completes, run the command again to start the application.
21
+
22
+ ## Usage
23
+
24
+ ```bash
25
+ # Start the GSD panel
26
+ npx get-shit-done-cc-ui
27
+
28
+ # The panel will open in your default browser at http://localhost:6942
29
+ ```
30
+
31
+ ## Requirements
32
+
33
+ - Node.js 16 or higher
34
+ - Internet connection (for first-run download only)
35
+
36
+ ## Supported Platforms
37
+
38
+ - Linux x64
39
+ - macOS Intel (x64)
40
+ - macOS Apple Silicon (arm64)
41
+ - Windows x64
42
+ - WSL (uses Linux binary)
43
+
44
+ ## Environment Variables
45
+
46
+ - `GSD_UI_PLATFORM` - Override platform detection (e.g., `darwin-arm64`, `linux-x64`, `win32-x64`)
47
+ - `NO_COLOR` - Disable colored output
48
+
49
+ ## Links
50
+
51
+ - [GitHub Repository](https://github.com/glennin-codes/get-shit-done-cc-ui)
52
+ - [Issue Tracker](https://github.com/glennin-codes/get-shit-done-cc-ui/issues)
53
+
54
+ ## Release Process (Maintainers)
55
+
56
+ ### Prerequisites
57
+
58
+ 1. **Create NPM_TOKEN:**
59
+ - Go to [npmjs.com](https://www.npmjs.com/) > Account > Access Tokens
60
+ - Click "Generate New Token" > "Automation" (for CI/CD)
61
+ - Copy the token value
62
+
63
+ 2. **Configure GitHub secret:**
64
+ - Go to GitHub repo > Settings > Secrets and variables > Actions
65
+ - Click "New repository secret"
66
+ - Name: `NPM_TOKEN`
67
+ - Value: paste the token from step 1
68
+
69
+ ### Releasing a new version
70
+
71
+ ```bash
72
+ # Update version in npm/package.json
73
+ ./scripts/bump-version.sh X.Y.Z
74
+
75
+ # Commit the version bump
76
+ git commit -am "chore: bump version to vX.Y.Z"
77
+
78
+ # Create and push tag
79
+ git tag vX.Y.Z
80
+ git push && git push --tags
81
+ ```
82
+
83
+ This triggers the release pipeline:
84
+ 1. Build binaries for all platforms
85
+ 2. Upload binaries to GitHub Releases
86
+ 3. Publish npm package
87
+
88
+ ## License
89
+
90
+ AGPL-3.0 - see LICENSE file in the repository
@@ -0,0 +1,19 @@
1
+ #!/bin/sh
2
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
3
+ BINARY="$SCRIPT_DIR/gsd-ui-web"
4
+
5
+ # Check if binary exists
6
+ if [ ! -f "$BINARY" ]; then
7
+ echo "Binary not found. Downloading for your platform..."
8
+ # Run download script via node
9
+ node "$SCRIPT_DIR/../lib/download.js"
10
+ DOWNLOAD_EXIT=$?
11
+ if [ $DOWNLOAD_EXIT -ne 0 ]; then
12
+ exit $DOWNLOAD_EXIT
13
+ fi
14
+ echo ""
15
+ echo "Download complete! Run 'npx get-shit-done-cc-ui' again to start."
16
+ exit 0
17
+ fi
18
+
19
+ exec "$BINARY" "$@"
@@ -0,0 +1,15 @@
1
+ @echo off
2
+ setlocal
3
+ set "SCRIPT_DIR=%~dp0"
4
+ set "BINARY=%SCRIPT_DIR%gsd-ui-web.exe"
5
+
6
+ if not exist "%BINARY%" (
7
+ echo Binary not found. Downloading for your platform...
8
+ node "%SCRIPT_DIR%..\lib\download.js"
9
+ if errorlevel 1 exit /b 1
10
+ echo.
11
+ echo Download complete! Run 'npx get-shit-done-cc-ui' again to start.
12
+ exit /b 0
13
+ )
14
+
15
+ "%BINARY%" %*
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env node
2
+
3
+ const https = require('https');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const crypto = require('crypto');
7
+ const ora = require('ora');
8
+ const supportsColor = require('supports-color');
9
+
10
+ const VERSION = require('../package.json').version;
11
+ const GITHUB_REPO = 'glennin-codes/get-shit-done-cc-ui';
12
+
13
+ /**
14
+ * Detect platform and return binary name
15
+ * @returns {string} Binary name (e.g., 'gsd-ui-web-linux-x64', 'gsd-ui-web.exe')
16
+ */
17
+ function detectPlatform() {
18
+ // Allow manual override via environment variable
19
+ const override = process.env.GSD_UI_PLATFORM;
20
+ if (override) {
21
+ return getPlatformBinaryName(override);
22
+ }
23
+
24
+ let platform = process.platform;
25
+ let arch = process.arch;
26
+
27
+ // WSL detection - check if running on Windows but with Linux kernel
28
+ if (platform === 'linux' && fs.existsSync('/proc/version')) {
29
+ const procVersion = fs.readFileSync('/proc/version', 'utf8').toLowerCase();
30
+ if (procVersion.includes('microsoft') || procVersion.includes('wsl')) {
31
+ // WSL detected, use Linux binary
32
+ platform = 'linux';
33
+ }
34
+ }
35
+
36
+ // Check if running on Windows but actually WSL (alternate detection)
37
+ if (platform === 'win32' && process.env.WSL_DISTRO_NAME) {
38
+ platform = 'linux';
39
+ }
40
+
41
+ const platformKey = `${platform}-${arch}`;
42
+ return getPlatformBinaryName(platformKey);
43
+ }
44
+
45
+ /**
46
+ * Map platform key to binary name
47
+ * @param {string} platformKey Platform identifier (e.g., 'linux-x64', 'darwin-arm64')
48
+ * @returns {string} Binary name
49
+ */
50
+ function getPlatformBinaryName(platformKey) {
51
+ const mapping = {
52
+ 'linux-x64': 'gsd-ui-web-linux-x64',
53
+ 'darwin-x64': 'gsd-ui-web-darwin-x64',
54
+ 'darwin-arm64': 'gsd-ui-web-darwin-arm64',
55
+ 'win32-x64': 'gsd-ui-web-win32-x64.exe',
56
+ };
57
+
58
+ const binaryName = mapping[platformKey];
59
+
60
+ if (!binaryName) {
61
+ const supportsColorStderr = supportsColor.stderr;
62
+ const red = supportsColorStderr && !process.env.NO_COLOR ? '\x1b[31m' : '';
63
+ const reset = supportsColorStderr && !process.env.NO_COLOR ? '\x1b[0m' : '';
64
+
65
+ console.error(`${red}Error: Unsupported platform: ${platformKey}${reset}`);
66
+ console.error('');
67
+ console.error('Supported platforms:');
68
+ console.error(' - linux-x64');
69
+ console.error(' - darwin-x64 (macOS Intel)');
70
+ console.error(' - darwin-arm64 (macOS Apple Silicon)');
71
+ console.error(' - win32-x64');
72
+ console.error('');
73
+ console.error('To build from source, visit:');
74
+ console.error('https://github.com/glennin-codes/get-shit-done-cc-ui#building-from-source');
75
+ process.exit(1);
76
+ }
77
+
78
+ return binaryName;
79
+ }
80
+
81
+ /**
82
+ * Download file from URL with progress
83
+ * @param {string} url URL to download from
84
+ * @param {string} destPath Destination file path
85
+ * @param {ora.Ora} spinner Spinner instance for progress updates
86
+ * @returns {Promise<void>}
87
+ */
88
+ function downloadFile(url, destPath, spinner) {
89
+ return new Promise((resolve, reject) => {
90
+ https.get(url, { followRedirect: true }, (response) => {
91
+ // Handle redirects
92
+ if (response.statusCode === 301 || response.statusCode === 302) {
93
+ return downloadFile(response.headers.location, destPath, spinner)
94
+ .then(resolve)
95
+ .catch(reject);
96
+ }
97
+
98
+ if (response.statusCode !== 200) {
99
+ reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
100
+ return;
101
+ }
102
+
103
+ const totalSize = parseInt(response.headers['content-length'], 10);
104
+ let downloadedSize = 0;
105
+
106
+ const file = fs.createWriteStream(destPath);
107
+
108
+ response.on('data', (chunk) => {
109
+ downloadedSize += chunk.length;
110
+ if (totalSize) {
111
+ const progress = ((downloadedSize / totalSize) * 100).toFixed(1);
112
+ const downloadedMB = (downloadedSize / 1024 / 1024).toFixed(1);
113
+ const totalMB = (totalSize / 1024 / 1024).toFixed(1);
114
+ spinner.text = `Downloading... ${downloadedMB}MB/${totalMB}MB (${progress}%)`;
115
+ }
116
+ });
117
+
118
+ response.pipe(file);
119
+
120
+ file.on('finish', () => {
121
+ file.close();
122
+ resolve();
123
+ });
124
+
125
+ file.on('error', (err) => {
126
+ fs.unlink(destPath, () => {}); // Clean up partial download
127
+ reject(err);
128
+ });
129
+ }).on('error', reject);
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Verify file checksum
135
+ * @param {string} filePath Path to file to verify
136
+ * @param {string} expectedChecksum Expected SHA256 checksum
137
+ * @returns {Promise<boolean>}
138
+ */
139
+ async function verifyChecksum(filePath, expectedChecksum) {
140
+ return new Promise((resolve, reject) => {
141
+ const hash = crypto.createHash('sha256');
142
+ const stream = fs.createReadStream(filePath);
143
+
144
+ stream.on('data', (data) => hash.update(data));
145
+ stream.on('end', () => {
146
+ const actualChecksum = hash.digest('hex');
147
+
148
+ // Use timing-safe comparison to prevent timing attacks
149
+ const expected = Buffer.from(expectedChecksum, 'hex');
150
+ const actual = Buffer.from(actualChecksum, 'hex');
151
+
152
+ if (expected.length !== actual.length) {
153
+ resolve(false);
154
+ return;
155
+ }
156
+
157
+ try {
158
+ const isValid = crypto.timingSafeEqual(expected, actual);
159
+ resolve(isValid);
160
+ } catch (err) {
161
+ reject(err);
162
+ }
163
+ });
164
+ stream.on('error', reject);
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Download and verify binary
170
+ * @returns {Promise<boolean>} True on success
171
+ */
172
+ async function downloadBinary() {
173
+ const supportsColorStdout = supportsColor.stdout;
174
+ const supportsColorStderr = supportsColor.stderr;
175
+ const hasColor = supportsColorStdout && !process.env.NO_COLOR;
176
+ const red = supportsColorStderr && !process.env.NO_COLOR ? '\x1b[31m' : '';
177
+ const yellow = supportsColorStderr && !process.env.NO_COLOR ? '\x1b[33m' : '';
178
+ const reset = supportsColorStderr && !process.env.NO_COLOR ? '\x1b[0m' : '';
179
+
180
+ const binaryName = detectPlatform();
181
+ const isWindows = binaryName.endsWith('.exe');
182
+ const outputName = isWindows ? 'gsd-ui-web.exe' : 'gsd-ui-web';
183
+
184
+ const binDir = path.join(__dirname, '..', 'bin');
185
+ const binaryPath = path.join(binDir, outputName);
186
+
187
+ // Ensure bin directory exists
188
+ if (!fs.existsSync(binDir)) {
189
+ fs.mkdirSync(binDir, { recursive: true });
190
+ }
191
+
192
+ const binaryUrl = `https://github.com/${GITHUB_REPO}/releases/download/v${VERSION}/${binaryName}`;
193
+ const checksumUrl = `${binaryUrl}.sha256`;
194
+
195
+ const spinner = ora({
196
+ text: 'Downloading binary...',
197
+ color: hasColor ? 'cyan' : undefined,
198
+ spinner: 'dots',
199
+ }).start();
200
+
201
+ try {
202
+ // Download checksum file first
203
+ spinner.text = 'Downloading checksum...';
204
+ const checksumPath = path.join(binDir, `${outputName}.sha256`);
205
+ await downloadFile(checksumUrl, checksumPath, spinner);
206
+ const expectedChecksum = fs.readFileSync(checksumPath, 'utf8').trim().split(/\s+/)[0];
207
+
208
+ // Download binary
209
+ spinner.text = 'Downloading binary...';
210
+ await downloadFile(binaryUrl, binaryPath, spinner);
211
+
212
+ // Verify checksum
213
+ spinner.text = 'Verifying checksum...';
214
+ const isValid = await verifyChecksum(binaryPath, expectedChecksum);
215
+
216
+ if (!isValid) {
217
+ spinner.fail('Checksum verification failed');
218
+ console.error(`${red}Error: Downloaded binary checksum does not match expected value.${reset}`);
219
+ console.error('This could indicate a corrupted download or security issue.');
220
+ console.error('');
221
+ console.error('Retry: npx get-shit-done-cc-ui');
222
+ fs.unlinkSync(binaryPath); // Remove invalid binary
223
+ fs.unlinkSync(checksumPath); // Remove checksum file
224
+ process.exit(1);
225
+ }
226
+
227
+ // Set executable permissions on Unix
228
+ if (!isWindows) {
229
+ fs.chmodSync(binaryPath, 0o755);
230
+ }
231
+
232
+ // Clean up checksum file
233
+ fs.unlinkSync(checksumPath);
234
+
235
+ spinner.succeed('Binary downloaded and verified successfully!');
236
+ return true;
237
+
238
+ } catch (error) {
239
+ spinner.fail('Download failed');
240
+ console.error('');
241
+ console.error(`${red}Error: ${error.message}${reset}`);
242
+ console.error('');
243
+ console.error('Check your internet connection and retry:');
244
+ console.error(' npx get-shit-done-cc-ui');
245
+
246
+ // Clean up any partial downloads
247
+ if (fs.existsSync(binaryPath)) {
248
+ fs.unlinkSync(binaryPath);
249
+ }
250
+ const checksumPath = path.join(binDir, `${outputName}.sha256`);
251
+ if (fs.existsSync(checksumPath)) {
252
+ fs.unlinkSync(checksumPath);
253
+ }
254
+
255
+ process.exit(1);
256
+ }
257
+ }
258
+
259
+ // Allow running directly for testing
260
+ if (require.main === module) {
261
+ if (process.argv.includes('--test-platform')) {
262
+ console.log(`Detected platform: ${detectPlatform()}`);
263
+ process.exit(0);
264
+ }
265
+
266
+ downloadBinary().catch((error) => {
267
+ console.error('Fatal error:', error);
268
+ process.exit(1);
269
+ });
270
+ }
271
+
272
+ module.exports = { downloadBinary };
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "get-shit-done-cc-ui",
3
+ "version": "0.2.2",
4
+ "description": "GSD-UI - Visual panel for Claude Code terminal workflows",
5
+ "bin": {
6
+ "get-shit-done-cc-ui": "./bin/get-shit-done-cc-ui"
7
+ },
8
+ "dependencies": {
9
+ "ora": "^8.1.1",
10
+ "supports-color": "^9.4.0"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/glennin-codes/get-shit-done-cc-ui.git"
15
+ },
16
+ "keywords": ["claude", "ai", "cli", "gsd", "terminal", "ui"],
17
+ "license": "AGPL-3.0",
18
+ "engines": {
19
+ "node": ">=16"
20
+ }
21
+ }