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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +553 -0
  3. package/cli.js +431 -0
  4. package/config-loader.js +294 -0
  5. package/hooks/activity-track.sh +56 -0
  6. package/hooks/codex-workstream.sh +44 -0
  7. package/hooks/gemini-workstream.sh +44 -0
  8. package/hooks/workstream-inject.sh +20 -0
  9. package/lib/activity.js +283 -0
  10. package/lib/apply.js +344 -0
  11. package/lib/cli.js +267 -0
  12. package/lib/config.js +171 -0
  13. package/lib/constants.js +55 -0
  14. package/lib/env.js +114 -0
  15. package/lib/index.js +47 -0
  16. package/lib/init.js +122 -0
  17. package/lib/mcps.js +139 -0
  18. package/lib/memory.js +201 -0
  19. package/lib/projects.js +138 -0
  20. package/lib/registry.js +83 -0
  21. package/lib/utils.js +129 -0
  22. package/lib/workstreams.js +652 -0
  23. package/package.json +80 -0
  24. package/scripts/capture-screenshots.js +142 -0
  25. package/scripts/postinstall.js +122 -0
  26. package/scripts/release.sh +71 -0
  27. package/scripts/sync-version.js +77 -0
  28. package/scripts/tauri-prepare.js +328 -0
  29. package/shared/mcp-registry.json +76 -0
  30. package/ui/dist/assets/index-DbZ3_HBD.js +3204 -0
  31. package/ui/dist/assets/index-DjLdm3Mr.css +32 -0
  32. package/ui/dist/icons/icon-192.svg +16 -0
  33. package/ui/dist/icons/icon-512.svg +16 -0
  34. package/ui/dist/index.html +39 -0
  35. package/ui/dist/manifest.json +25 -0
  36. package/ui/dist/sw.js +24 -0
  37. package/ui/dist/tutorial/claude-settings.png +0 -0
  38. package/ui/dist/tutorial/header.png +0 -0
  39. package/ui/dist/tutorial/mcp-registry.png +0 -0
  40. package/ui/dist/tutorial/memory-view.png +0 -0
  41. package/ui/dist/tutorial/permissions.png +0 -0
  42. package/ui/dist/tutorial/plugins-view.png +0 -0
  43. package/ui/dist/tutorial/project-explorer.png +0 -0
  44. package/ui/dist/tutorial/projects-view.png +0 -0
  45. package/ui/dist/tutorial/sidebar.png +0 -0
  46. package/ui/dist/tutorial/tutorial-view.png +0 -0
  47. package/ui/dist/tutorial/workstreams-view.png +0 -0
  48. package/ui/routes/activity.js +58 -0
  49. package/ui/routes/commands.js +74 -0
  50. package/ui/routes/configs.js +329 -0
  51. package/ui/routes/env.js +40 -0
  52. package/ui/routes/file-explorer.js +668 -0
  53. package/ui/routes/index.js +41 -0
  54. package/ui/routes/mcp-discovery.js +235 -0
  55. package/ui/routes/memory.js +385 -0
  56. package/ui/routes/package.json +3 -0
  57. package/ui/routes/plugins.js +466 -0
  58. package/ui/routes/projects.js +198 -0
  59. package/ui/routes/registry.js +30 -0
  60. package/ui/routes/rules.js +74 -0
  61. package/ui/routes/search.js +125 -0
  62. package/ui/routes/settings.js +381 -0
  63. package/ui/routes/subprojects.js +208 -0
  64. package/ui/routes/tool-sync.js +127 -0
  65. package/ui/routes/updates.js +339 -0
  66. package/ui/routes/workstreams.js +224 -0
  67. package/ui/server.cjs +773 -0
  68. 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!');