coder-config 0.40.1
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/LICENSE +21 -0
- package/README.md +553 -0
- package/cli.js +431 -0
- package/config-loader.js +294 -0
- package/hooks/activity-track.sh +56 -0
- package/hooks/codex-workstream.sh +44 -0
- package/hooks/gemini-workstream.sh +44 -0
- package/hooks/workstream-inject.sh +20 -0
- package/lib/activity.js +283 -0
- package/lib/apply.js +344 -0
- package/lib/cli.js +267 -0
- package/lib/config.js +171 -0
- package/lib/constants.js +55 -0
- package/lib/env.js +114 -0
- package/lib/index.js +47 -0
- package/lib/init.js +122 -0
- package/lib/mcps.js +139 -0
- package/lib/memory.js +201 -0
- package/lib/projects.js +138 -0
- package/lib/registry.js +83 -0
- package/lib/utils.js +129 -0
- package/lib/workstreams.js +652 -0
- package/package.json +80 -0
- package/scripts/capture-screenshots.js +142 -0
- package/scripts/postinstall.js +122 -0
- package/scripts/release.sh +71 -0
- package/scripts/sync-version.js +77 -0
- package/scripts/tauri-prepare.js +328 -0
- package/shared/mcp-registry.json +76 -0
- package/ui/dist/assets/index-DbZ3_HBD.js +3204 -0
- package/ui/dist/assets/index-DjLdm3Mr.css +32 -0
- package/ui/dist/icons/icon-192.svg +16 -0
- package/ui/dist/icons/icon-512.svg +16 -0
- package/ui/dist/index.html +39 -0
- package/ui/dist/manifest.json +25 -0
- package/ui/dist/sw.js +24 -0
- package/ui/dist/tutorial/claude-settings.png +0 -0
- package/ui/dist/tutorial/header.png +0 -0
- package/ui/dist/tutorial/mcp-registry.png +0 -0
- package/ui/dist/tutorial/memory-view.png +0 -0
- package/ui/dist/tutorial/permissions.png +0 -0
- package/ui/dist/tutorial/plugins-view.png +0 -0
- package/ui/dist/tutorial/project-explorer.png +0 -0
- package/ui/dist/tutorial/projects-view.png +0 -0
- package/ui/dist/tutorial/sidebar.png +0 -0
- package/ui/dist/tutorial/tutorial-view.png +0 -0
- package/ui/dist/tutorial/workstreams-view.png +0 -0
- package/ui/routes/activity.js +58 -0
- package/ui/routes/commands.js +74 -0
- package/ui/routes/configs.js +329 -0
- package/ui/routes/env.js +40 -0
- package/ui/routes/file-explorer.js +668 -0
- package/ui/routes/index.js +41 -0
- package/ui/routes/mcp-discovery.js +235 -0
- package/ui/routes/memory.js +385 -0
- package/ui/routes/package.json +3 -0
- package/ui/routes/plugins.js +466 -0
- package/ui/routes/projects.js +198 -0
- package/ui/routes/registry.js +30 -0
- package/ui/routes/rules.js +74 -0
- package/ui/routes/search.js +125 -0
- package/ui/routes/settings.js +381 -0
- package/ui/routes/subprojects.js +208 -0
- package/ui/routes/tool-sync.js +127 -0
- package/ui/routes/updates.js +339 -0
- package/ui/routes/workstreams.js +224 -0
- package/ui/server.cjs +773 -0
- package/ui/terminal-server.cjs +160 -0
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coder-config",
|
|
3
|
+
"version": "0.40.1",
|
|
4
|
+
"description": "Configuration manager for AI coding tools - Claude Code, Gemini CLI, Codex CLI, Antigravity. Manage MCPs, rules, permissions, memory, and workstreams.",
|
|
5
|
+
"author": "regression.io",
|
|
6
|
+
"main": "config-loader.js",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"bin": {
|
|
9
|
+
"coder-config": "cli.js",
|
|
10
|
+
"claude-config": "cli.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"start": "node cli.js ui",
|
|
14
|
+
"display": "node cli.js display",
|
|
15
|
+
"ui": "node cli.js ui",
|
|
16
|
+
"ui:dev": "cd ui && npm run dev",
|
|
17
|
+
"ui:build": "cd ui && npm run build",
|
|
18
|
+
"version:sync": "node scripts/sync-version.js",
|
|
19
|
+
"version:bump": "node scripts/sync-version.js --bump",
|
|
20
|
+
"build": "cd ui && npm install && npm run build",
|
|
21
|
+
"build:ci": "cd ui && npm install && npm run build",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"postinstall": "node scripts/postinstall.js",
|
|
24
|
+
"test": "node --test test/*.test.js",
|
|
25
|
+
"release": "./scripts/release.sh",
|
|
26
|
+
"tauri:prepare": "node scripts/tauri-prepare.js",
|
|
27
|
+
"tauri:dev": "npm run build && cargo tauri dev",
|
|
28
|
+
"tauri:build": "npm run tauri:prepare && cargo tauri build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"claude",
|
|
32
|
+
"claude-code",
|
|
33
|
+
"anthropic",
|
|
34
|
+
"gemini",
|
|
35
|
+
"codex",
|
|
36
|
+
"openai",
|
|
37
|
+
"antigravity",
|
|
38
|
+
"ai",
|
|
39
|
+
"coding-assistant",
|
|
40
|
+
"configuration",
|
|
41
|
+
"mcp",
|
|
42
|
+
"model-context-protocol",
|
|
43
|
+
"cli"
|
|
44
|
+
],
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@iarna/toml": "^3.0.0",
|
|
48
|
+
"js-yaml": "^4.1.1",
|
|
49
|
+
"node-pty": "^1.1.0",
|
|
50
|
+
"ws": "^8.19.0"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
},
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/regression-io/claude-config.git"
|
|
58
|
+
},
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/regression-io/claude-config/issues"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/regression-io/claude-config#readme",
|
|
63
|
+
"files": [
|
|
64
|
+
"cli.js",
|
|
65
|
+
"config-loader.js",
|
|
66
|
+
"lib/**",
|
|
67
|
+
"ui/server.cjs",
|
|
68
|
+
"ui/terminal-server.cjs",
|
|
69
|
+
"ui/routes/**",
|
|
70
|
+
"ui/dist/**",
|
|
71
|
+
"templates/**",
|
|
72
|
+
"shared/**",
|
|
73
|
+
"scripts/**",
|
|
74
|
+
"hooks/**",
|
|
75
|
+
"README.md"
|
|
76
|
+
],
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"playwright": "^1.57.0"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Capture screenshots for the tutorial using Playwright
|
|
4
|
+
* Run: node scripts/capture-screenshots.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { chromium } = require('playwright');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
|
|
11
|
+
const OUTPUT_DIR = path.join(__dirname, '..', 'ui', 'public', 'tutorial');
|
|
12
|
+
const BASE_URL = 'http://localhost:3333';
|
|
13
|
+
|
|
14
|
+
// Ensure output directory exists
|
|
15
|
+
if (!fs.existsSync(OUTPUT_DIR)) {
|
|
16
|
+
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function captureScreenshots() {
|
|
20
|
+
console.log('Launching browser...');
|
|
21
|
+
const browser = await chromium.launch({ headless: true });
|
|
22
|
+
const context = await browser.newContext({
|
|
23
|
+
viewport: { width: 1400, height: 900 },
|
|
24
|
+
deviceScaleFactor: 2, // Retina quality
|
|
25
|
+
});
|
|
26
|
+
const page = await context.newPage();
|
|
27
|
+
|
|
28
|
+
// Clear the welcome modal flag so we can capture it
|
|
29
|
+
await page.goto(BASE_URL);
|
|
30
|
+
await page.evaluate(() => localStorage.removeItem('claude-config-welcome-seen'));
|
|
31
|
+
await page.reload();
|
|
32
|
+
await page.waitForTimeout(1000);
|
|
33
|
+
|
|
34
|
+
// 1. Welcome Modal
|
|
35
|
+
console.log('Capturing: welcome-modal.png');
|
|
36
|
+
await page.waitForSelector('[role="dialog"]', { timeout: 5000 }).catch(() => {});
|
|
37
|
+
const welcomeModal = await page.$('[role="dialog"]');
|
|
38
|
+
if (welcomeModal) {
|
|
39
|
+
await welcomeModal.screenshot({ path: path.join(OUTPUT_DIR, 'welcome-modal.png') });
|
|
40
|
+
// Dismiss the modal
|
|
41
|
+
await page.click('button:has-text("Skip")');
|
|
42
|
+
await page.waitForTimeout(500);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2. Full sidebar
|
|
46
|
+
console.log('Capturing: sidebar.png');
|
|
47
|
+
const sidebar = await page.$('aside');
|
|
48
|
+
if (sidebar) {
|
|
49
|
+
await sidebar.screenshot({ path: path.join(OUTPUT_DIR, 'sidebar.png') });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 3. Project Switcher
|
|
53
|
+
console.log('Capturing: project-switcher.png');
|
|
54
|
+
await page.click('button:has-text("Select project")').catch(() => {});
|
|
55
|
+
await page.waitForTimeout(300);
|
|
56
|
+
const projectDropdown = await page.$('[role="listbox"], [data-radix-popper-content-wrapper]');
|
|
57
|
+
if (projectDropdown) {
|
|
58
|
+
await projectDropdown.screenshot({ path: path.join(OUTPUT_DIR, 'project-switcher.png') });
|
|
59
|
+
await page.keyboard.press('Escape');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 4. All Projects View
|
|
63
|
+
console.log('Capturing: projects-view.png');
|
|
64
|
+
await page.click('button:has-text("All Projects")');
|
|
65
|
+
await page.waitForTimeout(500);
|
|
66
|
+
const mainContent = await page.$('main');
|
|
67
|
+
if (mainContent) {
|
|
68
|
+
await mainContent.screenshot({ path: path.join(OUTPUT_DIR, 'projects-view.png') });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 5. Project Explorer
|
|
72
|
+
console.log('Capturing: project-explorer.png');
|
|
73
|
+
await page.click('button:has-text("Project Explorer")');
|
|
74
|
+
await page.waitForTimeout(500);
|
|
75
|
+
await mainContent?.screenshot({ path: path.join(OUTPUT_DIR, 'project-explorer.png') });
|
|
76
|
+
|
|
77
|
+
// 6. MCP Registry
|
|
78
|
+
console.log('Capturing: mcp-registry.png');
|
|
79
|
+
await page.click('button:has-text("MCP Registry")');
|
|
80
|
+
await page.waitForTimeout(500);
|
|
81
|
+
await mainContent?.screenshot({ path: path.join(OUTPUT_DIR, 'mcp-registry.png') });
|
|
82
|
+
|
|
83
|
+
// 7. Plugins View
|
|
84
|
+
console.log('Capturing: plugins-view.png');
|
|
85
|
+
await page.click('button:has-text("Plugins")');
|
|
86
|
+
await page.waitForTimeout(500);
|
|
87
|
+
await mainContent?.screenshot({ path: path.join(OUTPUT_DIR, 'plugins-view.png') });
|
|
88
|
+
|
|
89
|
+
// 8. Memory View
|
|
90
|
+
console.log('Capturing: memory-view.png');
|
|
91
|
+
await page.click('button:has-text("Memory")');
|
|
92
|
+
await page.waitForTimeout(500);
|
|
93
|
+
await mainContent?.screenshot({ path: path.join(OUTPUT_DIR, 'memory-view.png') });
|
|
94
|
+
|
|
95
|
+
// Dismiss any toasts/overlays that might be blocking
|
|
96
|
+
await page.keyboard.press('Escape');
|
|
97
|
+
await page.waitForTimeout(300);
|
|
98
|
+
|
|
99
|
+
// 9. Claude Code Settings
|
|
100
|
+
console.log('Capturing: claude-settings.png');
|
|
101
|
+
await page.click('button:has-text("Claude Code")', { force: true });
|
|
102
|
+
await page.waitForTimeout(800);
|
|
103
|
+
const mainAfterClaude = await page.$('main');
|
|
104
|
+
await mainAfterClaude?.screenshot({ path: path.join(OUTPUT_DIR, 'claude-settings.png') });
|
|
105
|
+
|
|
106
|
+
// 10. Permissions section specifically
|
|
107
|
+
console.log('Capturing: permissions.png');
|
|
108
|
+
const permissionsSection = await page.$('[class*="space-y"]');
|
|
109
|
+
if (permissionsSection) {
|
|
110
|
+
await permissionsSection.screenshot({ path: path.join(OUTPUT_DIR, 'permissions.png') });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 11. Workstreams View
|
|
114
|
+
console.log('Capturing: workstreams-view.png');
|
|
115
|
+
await page.click('button:has-text("Workstreams")', { force: true });
|
|
116
|
+
await page.waitForTimeout(800);
|
|
117
|
+
const mainAfterWorkstreams = await page.$('main');
|
|
118
|
+
await mainAfterWorkstreams?.screenshot({ path: path.join(OUTPUT_DIR, 'workstreams-view.png') });
|
|
119
|
+
|
|
120
|
+
// 12. Tutorial View
|
|
121
|
+
console.log('Capturing: tutorial-view.png');
|
|
122
|
+
await page.click('button:has-text("Tutorial")', { force: true });
|
|
123
|
+
await page.waitForTimeout(800);
|
|
124
|
+
const mainAfterTutorial = await page.$('main');
|
|
125
|
+
await mainAfterTutorial?.screenshot({ path: path.join(OUTPUT_DIR, 'tutorial-view.png') });
|
|
126
|
+
|
|
127
|
+
// 13. Header area
|
|
128
|
+
console.log('Capturing: header.png');
|
|
129
|
+
const header = await page.$('header');
|
|
130
|
+
if (header) {
|
|
131
|
+
await header.screenshot({ path: path.join(OUTPUT_DIR, 'header.png') });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
await browser.close();
|
|
135
|
+
console.log(`\nScreenshots saved to: ${OUTPUT_DIR}`);
|
|
136
|
+
console.log('Files:', fs.readdirSync(OUTPUT_DIR).join(', '));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
captureScreenshots().catch(err => {
|
|
140
|
+
console.error('Error capturing screenshots:', err);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post-install script for coder-config
|
|
5
|
+
* Sets up default configuration if not present
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
const home = os.homedir();
|
|
13
|
+
const claudeDir = path.join(home, '.claude');
|
|
14
|
+
const configPath = path.join(claudeDir, 'config.json');
|
|
15
|
+
|
|
16
|
+
// Default configuration
|
|
17
|
+
const defaultConfig = {
|
|
18
|
+
toolsDir: path.join(home, 'mcp-tools'),
|
|
19
|
+
registryPath: path.join(claudeDir, 'registry.json'),
|
|
20
|
+
ui: {
|
|
21
|
+
port: 3333,
|
|
22
|
+
openBrowser: true
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Create ~/.claude if it doesn't exist
|
|
27
|
+
if (!fs.existsSync(claudeDir)) {
|
|
28
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
29
|
+
console.log('Created ~/.claude directory');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Create config.json if it doesn't exist
|
|
33
|
+
if (!fs.existsSync(configPath)) {
|
|
34
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + '\n');
|
|
35
|
+
console.log('Created ~/.claude/config.json with defaults');
|
|
36
|
+
console.log(` Tools directory: ${defaultConfig.toolsDir}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create default registry if it doesn't exist
|
|
40
|
+
const registryPath = path.join(claudeDir, 'registry.json');
|
|
41
|
+
if (!fs.existsSync(registryPath)) {
|
|
42
|
+
const defaultRegistry = {
|
|
43
|
+
mcpServers: {}
|
|
44
|
+
};
|
|
45
|
+
fs.writeFileSync(registryPath, JSON.stringify(defaultRegistry, null, 2) + '\n');
|
|
46
|
+
console.log('Created ~/.claude/registry.json');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Test and rebuild node-pty if needed (prebuilds may not match Node version)
|
|
50
|
+
function testNodePty() {
|
|
51
|
+
const { execFileSync } = require('child_process');
|
|
52
|
+
// Use subprocess to avoid require cache issues
|
|
53
|
+
const testScript = `
|
|
54
|
+
try {
|
|
55
|
+
const pty = require('node-pty');
|
|
56
|
+
const p = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : '/bin/sh', ['-c', 'exit 0']);
|
|
57
|
+
p.kill();
|
|
58
|
+
process.exit(0);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
try {
|
|
64
|
+
execFileSync(process.execPath, ['-e', testScript], {
|
|
65
|
+
cwd: path.join(__dirname, '..'),
|
|
66
|
+
stdio: 'ignore'
|
|
67
|
+
});
|
|
68
|
+
return true;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function rebuildNodePty() {
|
|
75
|
+
const { execFileSync } = require('child_process');
|
|
76
|
+
const nodePtyPath = path.join(__dirname, '..', 'node_modules', 'node-pty');
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(nodePtyPath)) {
|
|
79
|
+
console.log('node-pty not installed, skipping rebuild check');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log('Testing node-pty compatibility...');
|
|
84
|
+
|
|
85
|
+
if (testNodePty()) {
|
|
86
|
+
console.log('node-pty working correctly');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log('node-pty prebuild incompatible with Node ' + process.version);
|
|
91
|
+
console.log('Rebuilding from source (this may take a minute)...');
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Use npm rebuild with build-from-source env var
|
|
95
|
+
execFileSync('npm', ['rebuild', 'node-pty'], {
|
|
96
|
+
cwd: path.join(__dirname, '..'),
|
|
97
|
+
stdio: 'inherit',
|
|
98
|
+
env: { ...process.env, npm_config_build_from_source: 'true' }
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (testNodePty()) {
|
|
102
|
+
console.log('node-pty rebuilt successfully');
|
|
103
|
+
} else {
|
|
104
|
+
console.warn('Warning: node-pty rebuild may have failed. Terminal features may not work.');
|
|
105
|
+
console.warn('Try: cd node_modules/node-pty && npm install --build-from-source');
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
console.warn('Warning: Could not rebuild node-pty:', e.message);
|
|
109
|
+
console.warn('Terminal features may not work. You may need build tools installed.');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
rebuildNodePty();
|
|
114
|
+
|
|
115
|
+
console.log('\n✓ Coder Config installed successfully!');
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log('Commands:');
|
|
118
|
+
console.log(' coder-config ui Start the web UI');
|
|
119
|
+
console.log(' coder-config init Initialize a project');
|
|
120
|
+
console.log(' coder-config --help Show all commands');
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log('Note: "claude-config" also works as an alias.\n');
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Auto-release script: bumps patch version, commits, tags, and pushes
|
|
3
|
+
# Usage: ./scripts/release.sh "commit message"
|
|
4
|
+
# ./scripts/release.sh "commit message" --minor
|
|
5
|
+
# ./scripts/release.sh "commit message" --major
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
if [ -z "$1" ]; then
|
|
10
|
+
echo "Usage: $0 \"commit message\" [--minor|--major]"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
MESSAGE="$1"
|
|
15
|
+
BUMP_TYPE="${2:---patch}"
|
|
16
|
+
|
|
17
|
+
# Get current version
|
|
18
|
+
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
19
|
+
|
|
20
|
+
# Parse version
|
|
21
|
+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
|
|
22
|
+
|
|
23
|
+
# Bump based on type
|
|
24
|
+
case "$BUMP_TYPE" in
|
|
25
|
+
--major)
|
|
26
|
+
MAJOR=$((MAJOR + 1))
|
|
27
|
+
MINOR=0
|
|
28
|
+
PATCH=0
|
|
29
|
+
;;
|
|
30
|
+
--minor)
|
|
31
|
+
MINOR=$((MINOR + 1))
|
|
32
|
+
PATCH=0
|
|
33
|
+
;;
|
|
34
|
+
--patch|*)
|
|
35
|
+
PATCH=$((PATCH + 1))
|
|
36
|
+
;;
|
|
37
|
+
esac
|
|
38
|
+
|
|
39
|
+
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
40
|
+
TAG="v$NEW_VERSION"
|
|
41
|
+
|
|
42
|
+
echo "Releasing $CURRENT_VERSION -> $NEW_VERSION"
|
|
43
|
+
|
|
44
|
+
# Update version in package.json
|
|
45
|
+
node -e "
|
|
46
|
+
const fs = require('fs');
|
|
47
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
48
|
+
pkg.version = '$NEW_VERSION';
|
|
49
|
+
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
50
|
+
"
|
|
51
|
+
|
|
52
|
+
# Sync version to other files
|
|
53
|
+
npm run version:sync
|
|
54
|
+
|
|
55
|
+
# Stage all changes
|
|
56
|
+
git add -A
|
|
57
|
+
|
|
58
|
+
# Commit
|
|
59
|
+
git commit -m "$MESSAGE
|
|
60
|
+
|
|
61
|
+
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
|
|
62
|
+
|
|
63
|
+
# Tag
|
|
64
|
+
git tag "$TAG"
|
|
65
|
+
|
|
66
|
+
# Push with tags
|
|
67
|
+
git push && git push --tags
|
|
68
|
+
|
|
69
|
+
echo ""
|
|
70
|
+
echo "✅ Released $TAG"
|
|
71
|
+
echo " CI will publish to npm automatically"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sync version from package.json to all version locations
|
|
5
|
+
* This ensures version is consistent across the codebase
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node sync-version.js # Sync current version
|
|
9
|
+
* node sync-version.js --bump # Bump patch version then sync
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const rootDir = path.join(__dirname, '..');
|
|
16
|
+
const packageJsonPath = path.join(rootDir, 'package.json');
|
|
17
|
+
const constantsPath = path.join(rootDir, 'lib', 'constants.js');
|
|
18
|
+
const uiPackageJsonPath = path.join(rootDir, 'ui', 'package.json');
|
|
19
|
+
const tauriConfigPath = path.join(rootDir, 'src-tauri', 'tauri.conf.json');
|
|
20
|
+
|
|
21
|
+
// Read version from package.json (source of truth)
|
|
22
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
23
|
+
let version = packageJson.version;
|
|
24
|
+
|
|
25
|
+
// Check for --bump flag
|
|
26
|
+
const shouldBump = process.argv.includes('--bump');
|
|
27
|
+
|
|
28
|
+
if (shouldBump) {
|
|
29
|
+
// Bump patch version
|
|
30
|
+
const parts = version.split('.');
|
|
31
|
+
parts[2] = parseInt(parts[2], 10) + 1;
|
|
32
|
+
version = parts.join('.');
|
|
33
|
+
|
|
34
|
+
// Update package.json with new version
|
|
35
|
+
packageJson.version = version;
|
|
36
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n', 'utf8');
|
|
37
|
+
console.log(`Bumped version: ${packageJson.version.replace(/\.\d+$/, `.${parseInt(parts[2]) - 1}`)} -> ${version}`);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(`Syncing version: ${version}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Update lib/constants.js
|
|
43
|
+
let constants = fs.readFileSync(constantsPath, 'utf8');
|
|
44
|
+
const versionRegex = /const VERSION = ['"][^'"]+['"]/;
|
|
45
|
+
|
|
46
|
+
if (versionRegex.test(constants)) {
|
|
47
|
+
const oldVersion = constants.match(versionRegex)[0];
|
|
48
|
+
constants = constants.replace(versionRegex, `const VERSION = '${version}'`);
|
|
49
|
+
fs.writeFileSync(constantsPath, constants, 'utf8');
|
|
50
|
+
console.log(` lib/constants.js: ${oldVersion} -> const VERSION = '${version}'`);
|
|
51
|
+
} else {
|
|
52
|
+
console.error('Warning: Could not find VERSION constant in lib/constants.js');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Update ui/package.json
|
|
56
|
+
if (fs.existsSync(uiPackageJsonPath)) {
|
|
57
|
+
const uiPackageJson = JSON.parse(fs.readFileSync(uiPackageJsonPath, 'utf8'));
|
|
58
|
+
if (uiPackageJson.version !== version) {
|
|
59
|
+
const oldVersion = uiPackageJson.version;
|
|
60
|
+
uiPackageJson.version = version;
|
|
61
|
+
fs.writeFileSync(uiPackageJsonPath, JSON.stringify(uiPackageJson, null, 2) + '\n', 'utf8');
|
|
62
|
+
console.log(` ui/package.json: ${oldVersion} -> ${version}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Update src-tauri/tauri.conf.json
|
|
67
|
+
if (fs.existsSync(tauriConfigPath)) {
|
|
68
|
+
const tauriConfig = JSON.parse(fs.readFileSync(tauriConfigPath, 'utf8'));
|
|
69
|
+
if (tauriConfig.version !== version) {
|
|
70
|
+
const oldVersion = tauriConfig.version;
|
|
71
|
+
tauriConfig.version = version;
|
|
72
|
+
fs.writeFileSync(tauriConfigPath, JSON.stringify(tauriConfig, null, 2) + '\n', 'utf8');
|
|
73
|
+
console.log(` src-tauri/tauri.conf.json: ${oldVersion} -> ${version}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('Version sync complete!');
|