coder-config 0.40.4 → 0.40.7
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/cli.js +16 -16
- package/config-loader.js +9 -9
- package/hooks/codex-workstream.sh +7 -6
- package/hooks/gemini-workstream.sh +6 -5
- package/lib/apply.js +1 -1
- package/lib/cli.js +2 -2
- package/lib/constants.js +1 -1
- package/lib/env.js +3 -3
- package/lib/init.js +2 -2
- package/lib/mcps.js +6 -6
- package/lib/memory.js +3 -3
- package/lib/projects.js +2 -2
- package/lib/registry.js +3 -3
- package/lib/workstreams.js +24 -23
- package/package.json +1 -1
- package/ui/dist/assets/{index-Pwkw3_R7.js → index-CTytYK0Q.js} +1 -1
- package/ui/dist/index.html +1 -1
- package/ui/routes/updates.js +2 -2
- package/ui/routes/workstreams.js +6 -3
package/cli.js
CHANGED
|
@@ -16,10 +16,10 @@ const args = process.argv.slice(2);
|
|
|
16
16
|
const command = args[0] || '';
|
|
17
17
|
|
|
18
18
|
// PID file for daemon mode
|
|
19
|
-
const PID_FILE = path.join(os.homedir(), '.
|
|
19
|
+
const PID_FILE = path.join(os.homedir(), '.coder-config', 'ui.pid');
|
|
20
20
|
|
|
21
21
|
// LaunchAgent for macOS auto-start
|
|
22
|
-
const LAUNCH_AGENT_LABEL = 'io.regression.
|
|
22
|
+
const LAUNCH_AGENT_LABEL = 'io.regression.coder-config';
|
|
23
23
|
const LAUNCH_AGENT_PATH = path.join(os.homedir(), 'Library', 'LaunchAgents', `${LAUNCH_AGENT_LABEL}.plist`);
|
|
24
24
|
|
|
25
25
|
// UI command needs special handling (starts web server with better error handling)
|
|
@@ -108,10 +108,10 @@ function checkDaemonStatus() {
|
|
|
108
108
|
// Check if LaunchAgent exists but not running
|
|
109
109
|
if (process.platform === 'darwin' && fs.existsSync(LAUNCH_AGENT_PATH)) {
|
|
110
110
|
console.log('Daemon: not running (LaunchAgent installed but stopped)');
|
|
111
|
-
console.log('Run: launchctl load ~/Library/LaunchAgents/io.regression.
|
|
111
|
+
console.log('Run: launchctl load ~/Library/LaunchAgents/io.regression.coder-config.plist');
|
|
112
112
|
} else {
|
|
113
113
|
console.log('Daemon: not running');
|
|
114
|
-
console.log('Run:
|
|
114
|
+
console.log('Run: coder-config ui');
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -122,10 +122,10 @@ function installLaunchAgent() {
|
|
|
122
122
|
process.exit(1);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
// Find the
|
|
125
|
+
// Find the coder-config executable
|
|
126
126
|
let execPath;
|
|
127
127
|
try {
|
|
128
|
-
execPath = execSync('which
|
|
128
|
+
execPath = execSync('which coder-config', { encoding: 'utf8' }).trim();
|
|
129
129
|
} catch {
|
|
130
130
|
execPath = path.join(__dirname, 'cli.js');
|
|
131
131
|
}
|
|
@@ -161,9 +161,9 @@ function installLaunchAgent() {
|
|
|
161
161
|
<key>KeepAlive</key>
|
|
162
162
|
<true/>
|
|
163
163
|
<key>StandardOutPath</key>
|
|
164
|
-
<string>${path.join(os.homedir(), '.
|
|
164
|
+
<string>${path.join(os.homedir(), '.coder-config', 'ui.log')}</string>
|
|
165
165
|
<key>StandardErrorPath</key>
|
|
166
|
-
<string>${path.join(os.homedir(), '.
|
|
166
|
+
<string>${path.join(os.homedir(), '.coder-config', 'ui.log')}</string>
|
|
167
167
|
<key>WorkingDirectory</key>
|
|
168
168
|
<string>${os.homedir()}</string>
|
|
169
169
|
</dict>
|
|
@@ -176,7 +176,7 @@ function installLaunchAgent() {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
// Ensure log directory exists
|
|
179
|
-
const logDir = path.join(os.homedir(), '.
|
|
179
|
+
const logDir = path.join(os.homedir(), '.coder-config');
|
|
180
180
|
if (!fs.existsSync(logDir)) {
|
|
181
181
|
fs.mkdirSync(logDir, { recursive: true });
|
|
182
182
|
}
|
|
@@ -223,8 +223,8 @@ function installLaunchAgent() {
|
|
|
223
223
|
console.log('Your PWA can now connect anytime!');
|
|
224
224
|
console.log('');
|
|
225
225
|
console.log('Commands:');
|
|
226
|
-
console.log('
|
|
227
|
-
console.log('
|
|
226
|
+
console.log(' coder-config ui status - Check if running');
|
|
227
|
+
console.log(' coder-config ui uninstall - Remove auto-start');
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
function uninstallLaunchAgent() {
|
|
@@ -249,7 +249,7 @@ function uninstallLaunchAgent() {
|
|
|
249
249
|
|
|
250
250
|
console.log('✓ Removed auto-start for Coder Config UI');
|
|
251
251
|
console.log('');
|
|
252
|
-
console.log('To start manually:
|
|
252
|
+
console.log('To start manually: coder-config ui');
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
function startDaemon(flags) {
|
|
@@ -260,7 +260,7 @@ function startDaemon(flags) {
|
|
|
260
260
|
process.kill(existingPid, 0);
|
|
261
261
|
console.log(`Daemon already running (PID: ${existingPid})`);
|
|
262
262
|
console.log(`UI available at: http://localhost:${flags.port}`);
|
|
263
|
-
console.log('Use "
|
|
263
|
+
console.log('Use "coder-config ui stop" to stop the daemon');
|
|
264
264
|
return;
|
|
265
265
|
} catch (err) {
|
|
266
266
|
// Process not running, clean up stale PID file
|
|
@@ -306,8 +306,8 @@ function startDaemon(flags) {
|
|
|
306
306
|
console.log(`UI available at: http://localhost:${flags.port}`);
|
|
307
307
|
console.log(`Logs: ${logFile}`);
|
|
308
308
|
console.log('\nCommands:');
|
|
309
|
-
console.log('
|
|
310
|
-
console.log('
|
|
309
|
+
console.log(' coder-config ui status - Check daemon status');
|
|
310
|
+
console.log(' coder-config ui stop - Stop the daemon');
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
function startUI() {
|
|
@@ -407,7 +407,7 @@ function startUI() {
|
|
|
407
407
|
process.on('uncaughtException', (err) => {
|
|
408
408
|
if (err.code === 'EADDRINUSE') {
|
|
409
409
|
console.error(`\nError: Port ${flags.port} is already in use.`);
|
|
410
|
-
console.error(`Try a different port:
|
|
410
|
+
console.error(`Try a different port: coder-config ui --port ${flags.port + 1}`);
|
|
411
411
|
process.exit(1);
|
|
412
412
|
} else if (err.code === 'EACCES') {
|
|
413
413
|
console.error(`\nError: Permission denied for port ${flags.port}.`);
|
package/config-loader.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Coder Config - Configuration Loader
|
|
5
5
|
*
|
|
6
6
|
* Uses standard JSON format throughout - no custom YAML.
|
|
7
7
|
* Copy/paste MCP configs from anywhere.
|
|
8
8
|
*
|
|
9
9
|
* Files:
|
|
10
|
-
* ~/.
|
|
10
|
+
* ~/.coder-config/mcp-registry.json - All available MCPs (copy/paste friendly)
|
|
11
11
|
* project/.claude/mcps.json - Which MCPs this project uses
|
|
12
12
|
* project/.claude/rules/*.md - Project rules
|
|
13
13
|
* project/.claude/commands/*.md - Project commands
|
|
@@ -34,7 +34,7 @@ const { runCli } = require('./lib/cli');
|
|
|
34
34
|
|
|
35
35
|
class ClaudeConfigManager {
|
|
36
36
|
constructor() {
|
|
37
|
-
this.installDir = process.env.CLAUDE_CONFIG_HOME || path.join(process.env.HOME || '', '.
|
|
37
|
+
this.installDir = process.env.CLAUDE_CONFIG_HOME || path.join(process.env.HOME || '', '.coder-config');
|
|
38
38
|
|
|
39
39
|
// Look for registry in multiple places
|
|
40
40
|
const possiblePaths = [
|
|
@@ -180,23 +180,23 @@ class ClaudeConfigManager {
|
|
|
180
180
|
console.log(`\nUpdate available: v${VERSION} → v${npmVersion}`);
|
|
181
181
|
|
|
182
182
|
if (checkOnly) {
|
|
183
|
-
console.log('\nRun "
|
|
183
|
+
console.log('\nRun "coder-config update" to install the update.');
|
|
184
184
|
return true;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
// Perform npm update
|
|
188
188
|
console.log('\nUpdating via npm...');
|
|
189
189
|
try {
|
|
190
|
-
execSync('npm install -g
|
|
190
|
+
execSync('npm install -g coder-config@latest', {
|
|
191
191
|
stdio: 'inherit',
|
|
192
192
|
timeout: 120000
|
|
193
193
|
});
|
|
194
194
|
console.log(`\n✅ Updated to v${npmVersion}`);
|
|
195
|
-
console.log('Run "
|
|
195
|
+
console.log('Run "coder-config ui" to restart the UI with the new version.');
|
|
196
196
|
return true;
|
|
197
197
|
} catch (error) {
|
|
198
198
|
console.error('Update failed:', error.message);
|
|
199
|
-
console.log('\nTry manually: npm install -g
|
|
199
|
+
console.log('\nTry manually: npm install -g coder-config@latest');
|
|
200
200
|
return false;
|
|
201
201
|
}
|
|
202
202
|
}
|
|
@@ -204,7 +204,7 @@ class ClaudeConfigManager {
|
|
|
204
204
|
// Fetch latest version from npm registry
|
|
205
205
|
_fetchNpmVersion(https) {
|
|
206
206
|
return new Promise((resolve) => {
|
|
207
|
-
const url = 'https://registry.npmjs.org
|
|
207
|
+
const url = 'https://registry.npmjs.org/coder-config/latest';
|
|
208
208
|
https.get(url, (res) => {
|
|
209
209
|
let data = '';
|
|
210
210
|
res.on('data', chunk => data += chunk);
|
|
@@ -245,7 +245,7 @@ class ClaudeConfigManager {
|
|
|
245
245
|
const files = [
|
|
246
246
|
'config-loader.js',
|
|
247
247
|
'shared/mcp-registry.json',
|
|
248
|
-
'shell/
|
|
248
|
+
'shell/coder-config.zsh'
|
|
249
249
|
];
|
|
250
250
|
|
|
251
251
|
let updated = 0;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Codex CLI Workstream Injection Hook
|
|
3
|
-
# Install:
|
|
3
|
+
# Install: coder-config workstream install-hook --codex
|
|
4
4
|
#
|
|
5
5
|
# This hook injects workstream context at session start, restricting
|
|
6
6
|
# the agent to work within specified directories.
|
|
7
7
|
|
|
8
|
-
# Exit silently if
|
|
9
|
-
if ! command -v
|
|
8
|
+
# Exit silently if coder-config isn't available
|
|
9
|
+
if ! command -v coder-config &> /dev/null; then
|
|
10
10
|
echo '{"decision": "allow"}'
|
|
11
11
|
exit 0
|
|
12
12
|
fi
|
|
@@ -14,15 +14,16 @@ fi
|
|
|
14
14
|
# Read input from stdin (Codex passes JSON)
|
|
15
15
|
input=$(cat)
|
|
16
16
|
|
|
17
|
-
# Check for active workstream via env var
|
|
18
|
-
|
|
17
|
+
# Check for active workstream via env var (CODER_WORKSTREAM preferred, CLAUDE_WORKSTREAM legacy)
|
|
18
|
+
WORKSTREAM="${CODER_WORKSTREAM:-$CLAUDE_WORKSTREAM}"
|
|
19
|
+
if [ -z "$WORKSTREAM" ]; then
|
|
19
20
|
# No workstream active, allow without context
|
|
20
21
|
echo '{"decision": "allow"}'
|
|
21
22
|
exit 0
|
|
22
23
|
fi
|
|
23
24
|
|
|
24
25
|
# Get workstream injection content
|
|
25
|
-
context=$(
|
|
26
|
+
context=$(coder-config workstream inject --silent 2>/dev/null)
|
|
26
27
|
|
|
27
28
|
if [ -n "$context" ]; then
|
|
28
29
|
# Escape the context for JSON
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
# This hook injects workstream context at session start, restricting
|
|
6
6
|
# the agent to work within specified directories.
|
|
7
7
|
|
|
8
|
-
# Exit silently if
|
|
9
|
-
if ! command -v
|
|
8
|
+
# Exit silently if coder-config isn't available
|
|
9
|
+
if ! command -v coder-config &> /dev/null; then
|
|
10
10
|
echo '{"decision": "allow"}'
|
|
11
11
|
exit 0
|
|
12
12
|
fi
|
|
@@ -14,15 +14,16 @@ fi
|
|
|
14
14
|
# Read input from stdin (Gemini passes JSON)
|
|
15
15
|
input=$(cat)
|
|
16
16
|
|
|
17
|
-
# Check for active workstream via env var
|
|
18
|
-
|
|
17
|
+
# Check for active workstream via env var (CODER_WORKSTREAM preferred, CLAUDE_WORKSTREAM legacy)
|
|
18
|
+
WORKSTREAM="${CODER_WORKSTREAM:-$CLAUDE_WORKSTREAM}"
|
|
19
|
+
if [ -z "$WORKSTREAM" ]; then
|
|
19
20
|
# No workstream active, allow without context
|
|
20
21
|
echo '{"decision": "allow"}'
|
|
21
22
|
exit 0
|
|
22
23
|
fi
|
|
23
24
|
|
|
24
25
|
# Get workstream injection content
|
|
25
|
-
context=$(
|
|
26
|
+
context=$(coder-config workstream inject --silent 2>/dev/null)
|
|
26
27
|
|
|
27
28
|
if [ -n "$context" ]; then
|
|
28
29
|
# Escape the context for JSON
|
package/lib/apply.js
CHANGED
|
@@ -25,7 +25,7 @@ function apply(registryPath, projectDir = null) {
|
|
|
25
25
|
|
|
26
26
|
if (configLocations.length === 0) {
|
|
27
27
|
console.error(`No .claude/mcps.json found in ${dir} or parent directories`);
|
|
28
|
-
console.error('Run:
|
|
28
|
+
console.error('Run: coder-config init');
|
|
29
29
|
return false;
|
|
30
30
|
}
|
|
31
31
|
|
package/lib/cli.js
CHANGED
|
@@ -138,7 +138,7 @@ function runCli(manager) {
|
|
|
138
138
|
} else if (args[1] === 'check-path') {
|
|
139
139
|
const targetPath = args[2];
|
|
140
140
|
if (!targetPath) {
|
|
141
|
-
console.error('Usage:
|
|
141
|
+
console.error('Usage: coder-config workstream check-path <path>');
|
|
142
142
|
process.exit(1);
|
|
143
143
|
}
|
|
144
144
|
const silent = args.includes('--silent') || args.includes('-s');
|
|
@@ -230,7 +230,7 @@ Workstream Commands:
|
|
|
230
230
|
workstream install-hook --all Install for all supported tools
|
|
231
231
|
|
|
232
232
|
Per-session activation (enables parallel work):
|
|
233
|
-
export
|
|
233
|
+
export CODER_WORKSTREAM=<name-or-id>
|
|
234
234
|
|
|
235
235
|
Registry Commands:
|
|
236
236
|
registry List MCPs in global registry
|
package/lib/constants.js
CHANGED
package/lib/env.js
CHANGED
|
@@ -15,7 +15,7 @@ function envList(projectDir = process.cwd()) {
|
|
|
15
15
|
|
|
16
16
|
if (!fs.existsSync(envPath)) {
|
|
17
17
|
console.log(' No .env file found.');
|
|
18
|
-
console.log(' Create with:
|
|
18
|
+
console.log(' Create with: coder-config env set <KEY> <value>\n');
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -41,7 +41,7 @@ function envList(projectDir = process.cwd()) {
|
|
|
41
41
|
*/
|
|
42
42
|
function envSet(key, value, projectDir = process.cwd()) {
|
|
43
43
|
if (!key || value === undefined) {
|
|
44
|
-
console.error('Usage:
|
|
44
|
+
console.error('Usage: coder-config env set <KEY> <value>');
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -81,7 +81,7 @@ function envSet(key, value, projectDir = process.cwd()) {
|
|
|
81
81
|
*/
|
|
82
82
|
function envUnset(key, projectDir = process.cwd()) {
|
|
83
83
|
if (!key) {
|
|
84
|
-
console.error('Usage:
|
|
84
|
+
console.error('Usage: coder-config env unset <KEY>');
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
|
package/lib/init.js
CHANGED
|
@@ -51,9 +51,9 @@ function init(registryPath, projectDir = null) {
|
|
|
51
51
|
console.log('\n✅ Project initialized!');
|
|
52
52
|
console.log('Next steps:');
|
|
53
53
|
console.log(' 1. Edit .claude/mcps.json to customize MCPs');
|
|
54
|
-
console.log(' 2. Run:
|
|
54
|
+
console.log(' 2. Run: coder-config ui');
|
|
55
55
|
console.log(' (Plugins marketplace auto-installs on first use)');
|
|
56
|
-
console.log(' 3. Run:
|
|
56
|
+
console.log(' 3. Run: coder-config apply\n');
|
|
57
57
|
|
|
58
58
|
return true;
|
|
59
59
|
}
|
package/lib/mcps.js
CHANGED
|
@@ -37,7 +37,7 @@ function list(registryPath) {
|
|
|
37
37
|
*/
|
|
38
38
|
function add(registryPath, installDir, mcpNames) {
|
|
39
39
|
if (!mcpNames || mcpNames.length === 0) {
|
|
40
|
-
console.error('Usage:
|
|
40
|
+
console.error('Usage: coder-config add <mcp-name> [mcp-name...]');
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -46,7 +46,7 @@ function add(registryPath, installDir, mcpNames) {
|
|
|
46
46
|
let config = loadJson(configPath);
|
|
47
47
|
|
|
48
48
|
if (!config) {
|
|
49
|
-
console.error('No .claude/mcps.json found. Run:
|
|
49
|
+
console.error('No .claude/mcps.json found. Run: coder-config init');
|
|
50
50
|
return false;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -77,11 +77,11 @@ function add(registryPath, installDir, mcpNames) {
|
|
|
77
77
|
}
|
|
78
78
|
if (notFound.length) {
|
|
79
79
|
console.log(`Not in registry: ${notFound.join(', ')}`);
|
|
80
|
-
console.log(' (Use "
|
|
80
|
+
console.log(' (Use "coder-config list" to see available MCPs)');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
if (added.length) {
|
|
84
|
-
console.log('\nRun "
|
|
84
|
+
console.log('\nRun "coder-config apply" to regenerate .mcp.json');
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
return added.length > 0;
|
|
@@ -92,7 +92,7 @@ function add(registryPath, installDir, mcpNames) {
|
|
|
92
92
|
*/
|
|
93
93
|
function remove(installDir, mcpNames) {
|
|
94
94
|
if (!mcpNames || mcpNames.length === 0) {
|
|
95
|
-
console.error('Usage:
|
|
95
|
+
console.error('Usage: coder-config remove <mcp-name> [mcp-name...]');
|
|
96
96
|
return false;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -123,7 +123,7 @@ function remove(installDir, mcpNames) {
|
|
|
123
123
|
if (removed.length) {
|
|
124
124
|
saveJson(configPath, config);
|
|
125
125
|
console.log(`✓ Removed: ${removed.join(', ')}`);
|
|
126
|
-
console.log('\nRun "
|
|
126
|
+
console.log('\nRun "coder-config apply" to regenerate .mcp.json');
|
|
127
127
|
}
|
|
128
128
|
if (notFound.length) {
|
|
129
129
|
console.log(`Not in project: ${notFound.join(', ')}`);
|
package/lib/memory.js
CHANGED
|
@@ -46,7 +46,7 @@ function memoryList(projectDir = process.cwd()) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
} else {
|
|
49
|
-
console.log(' Not initialized. Run:
|
|
49
|
+
console.log(' Not initialized. Run: coder-config memory init');
|
|
50
50
|
}
|
|
51
51
|
console.log();
|
|
52
52
|
}
|
|
@@ -88,7 +88,7 @@ function memoryInit(projectDir = process.cwd()) {
|
|
|
88
88
|
*/
|
|
89
89
|
function memoryAdd(type, content, projectDir = process.cwd()) {
|
|
90
90
|
if (!type || !content) {
|
|
91
|
-
console.error('Usage:
|
|
91
|
+
console.error('Usage: coder-config memory add <type> "<content>"');
|
|
92
92
|
console.log('\nTypes:');
|
|
93
93
|
console.log(' Global: preference, correction, fact');
|
|
94
94
|
console.log(' Project: context, pattern, decision, issue, history');
|
|
@@ -147,7 +147,7 @@ function memoryAdd(type, content, projectDir = process.cwd()) {
|
|
|
147
147
|
*/
|
|
148
148
|
function memorySearch(query, projectDir = process.cwd()) {
|
|
149
149
|
if (!query) {
|
|
150
|
-
console.error('Usage:
|
|
150
|
+
console.error('Usage: coder-config memory search <query>');
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
153
|
|
package/lib/projects.js
CHANGED
|
@@ -44,7 +44,7 @@ function projectList(installDir) {
|
|
|
44
44
|
|
|
45
45
|
if (registry.projects.length === 0) {
|
|
46
46
|
console.log('\nNo projects registered.');
|
|
47
|
-
console.log('Add one with:
|
|
47
|
+
console.log('Add one with: coder-config project add [path]\n');
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -101,7 +101,7 @@ function projectAdd(installDir, projectPath = process.cwd(), name = null) {
|
|
|
101
101
|
*/
|
|
102
102
|
function projectRemove(installDir, nameOrPath) {
|
|
103
103
|
if (!nameOrPath) {
|
|
104
|
-
console.error('Usage:
|
|
104
|
+
console.error('Usage: coder-config project remove <name|path>');
|
|
105
105
|
return false;
|
|
106
106
|
}
|
|
107
107
|
|
package/lib/registry.js
CHANGED
|
@@ -14,7 +14,7 @@ function registryList(registryPath) {
|
|
|
14
14
|
|
|
15
15
|
if (names.length === 0) {
|
|
16
16
|
console.log('\nNo MCPs in global registry.');
|
|
17
|
-
console.log('Add one with:
|
|
17
|
+
console.log('Add one with: coder-config registry add <name> \'{"command":"..."}\'');
|
|
18
18
|
return [];
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -34,7 +34,7 @@ function registryList(registryPath) {
|
|
|
34
34
|
*/
|
|
35
35
|
function registryAdd(registryPath, name, configJson) {
|
|
36
36
|
if (!name || !configJson) {
|
|
37
|
-
console.error('Usage:
|
|
37
|
+
console.error('Usage: coder-config registry add <name> \'{"command":"...","args":[...]}\'');
|
|
38
38
|
return false;
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -59,7 +59,7 @@ function registryAdd(registryPath, name, configJson) {
|
|
|
59
59
|
*/
|
|
60
60
|
function registryRemove(registryPath, name) {
|
|
61
61
|
if (!name) {
|
|
62
|
-
console.error('Usage:
|
|
62
|
+
console.error('Usage: coder-config registry remove <name>');
|
|
63
63
|
return false;
|
|
64
64
|
}
|
|
65
65
|
|
package/lib/workstreams.js
CHANGED
|
@@ -47,7 +47,7 @@ function workstreamList(installDir) {
|
|
|
47
47
|
|
|
48
48
|
if (data.workstreams.length === 0) {
|
|
49
49
|
console.log('\nNo workstreams defined.');
|
|
50
|
-
console.log('Create one with:
|
|
50
|
+
console.log('Create one with: coder-config workstream create "Name"\n');
|
|
51
51
|
return data.workstreams;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -72,7 +72,7 @@ function workstreamList(installDir) {
|
|
|
72
72
|
*/
|
|
73
73
|
function workstreamCreate(installDir, name, projects = [], rules = '') {
|
|
74
74
|
if (!name) {
|
|
75
|
-
console.error('Usage:
|
|
75
|
+
console.error('Usage: coder-config workstream create "Name"');
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -258,7 +258,8 @@ function getActiveWorkstream(installDir) {
|
|
|
258
258
|
const data = loadWorkstreams(installDir);
|
|
259
259
|
|
|
260
260
|
// Check env var first (per-session activation)
|
|
261
|
-
|
|
261
|
+
// Support both CODER_WORKSTREAM (preferred) and CLAUDE_WORKSTREAM (legacy)
|
|
262
|
+
const envWorkstream = process.env.CODER_WORKSTREAM || process.env.CLAUDE_WORKSTREAM;
|
|
262
263
|
if (envWorkstream) {
|
|
263
264
|
const ws = data.workstreams.find(
|
|
264
265
|
w => w.id === envWorkstream || w.name.toLowerCase() === envWorkstream.toLowerCase()
|
|
@@ -396,18 +397,18 @@ function workstreamInstallHook() {
|
|
|
396
397
|
|
|
397
398
|
const hookContent = `#!/bin/bash
|
|
398
399
|
# Claude Code pre-prompt hook for workstream injection
|
|
399
|
-
# Installed by
|
|
400
|
+
# Installed by coder-config
|
|
400
401
|
|
|
401
402
|
# Check for active workstream via env var or file
|
|
402
|
-
if [ -n "$
|
|
403
|
-
|
|
403
|
+
if [ -n "$CODER_WORKSTREAM" ] || coder-config workstream active >/dev/null 2>&1; then
|
|
404
|
+
coder-config workstream inject --silent
|
|
404
405
|
fi
|
|
405
406
|
`;
|
|
406
407
|
|
|
407
408
|
// Check if hook already exists
|
|
408
409
|
if (fs.existsSync(hookPath)) {
|
|
409
410
|
const existing = fs.readFileSync(hookPath, 'utf8');
|
|
410
|
-
if (existing.includes('
|
|
411
|
+
if (existing.includes('coder-config workstream inject')) {
|
|
411
412
|
console.log('✓ Workstream hook already installed');
|
|
412
413
|
return true;
|
|
413
414
|
}
|
|
@@ -423,9 +424,9 @@ fi
|
|
|
423
424
|
console.log('\nWorkstream injection is now active. When a workstream is active,');
|
|
424
425
|
console.log('Claude will see the restriction and context at the start of each prompt.');
|
|
425
426
|
console.log('\nTo activate a workstream for this session:');
|
|
426
|
-
console.log(' export
|
|
427
|
+
console.log(' export CODER_WORKSTREAM=<name-or-id>');
|
|
427
428
|
console.log('\nOr use the global active workstream:');
|
|
428
|
-
console.log('
|
|
429
|
+
console.log(' coder-config workstream use <name>');
|
|
429
430
|
|
|
430
431
|
return true;
|
|
431
432
|
}
|
|
@@ -452,13 +453,13 @@ function workstreamInstallHookGemini() {
|
|
|
452
453
|
}
|
|
453
454
|
}
|
|
454
455
|
|
|
455
|
-
// Find the hook script path (relative to
|
|
456
|
+
// Find the hook script path (relative to coder-config installation)
|
|
456
457
|
const hookScriptPath = path.join(__dirname, '..', 'hooks', 'gemini-workstream.sh');
|
|
457
458
|
|
|
458
459
|
// Check if hook already installed
|
|
459
460
|
const existingHooks = settings.hooks?.SessionStart || [];
|
|
460
461
|
const alreadyInstalled = existingHooks.some(h =>
|
|
461
|
-
h.name === '
|
|
462
|
+
h.name === 'coder-config-workstream' || (h.command && h.command.includes('gemini-workstream'))
|
|
462
463
|
);
|
|
463
464
|
|
|
464
465
|
if (alreadyInstalled) {
|
|
@@ -478,7 +479,7 @@ function workstreamInstallHookGemini() {
|
|
|
478
479
|
}
|
|
479
480
|
|
|
480
481
|
settings.hooks.SessionStart.push({
|
|
481
|
-
name: '
|
|
482
|
+
name: 'coder-config-workstream',
|
|
482
483
|
type: 'command',
|
|
483
484
|
command: hookScriptPath,
|
|
484
485
|
description: 'Inject workstream context and restrictions',
|
|
@@ -495,7 +496,7 @@ function workstreamInstallHookGemini() {
|
|
|
495
496
|
console.log('When a workstream is active, Gemini will see the restriction');
|
|
496
497
|
console.log('and context at the start of each session.');
|
|
497
498
|
console.log('\nTo activate a workstream for this session:');
|
|
498
|
-
console.log(' export
|
|
499
|
+
console.log(' export CODER_WORKSTREAM=<name-or-id>');
|
|
499
500
|
|
|
500
501
|
return true;
|
|
501
502
|
}
|
|
@@ -534,15 +535,15 @@ function workstreamInstallHookCodex() {
|
|
|
534
535
|
// Check if hook already exists with our content
|
|
535
536
|
if (fs.existsSync(targetHookPath)) {
|
|
536
537
|
const existing = fs.readFileSync(targetHookPath, 'utf8');
|
|
537
|
-
if (existing.includes('
|
|
538
|
+
if (existing.includes('coder-config workstream inject')) {
|
|
538
539
|
console.log('✓ Workstream hook already installed for Codex CLI');
|
|
539
540
|
return true;
|
|
540
541
|
}
|
|
541
542
|
// Append to existing hook
|
|
542
543
|
const appendContent = `
|
|
543
|
-
#
|
|
544
|
-
if [ -n "$
|
|
545
|
-
|
|
544
|
+
# coder-config workstream injection
|
|
545
|
+
if [ -n "$CODER_WORKSTREAM" ] && command -v coder-config &> /dev/null; then
|
|
546
|
+
coder-config workstream inject --silent
|
|
546
547
|
fi
|
|
547
548
|
`;
|
|
548
549
|
fs.appendFileSync(targetHookPath, appendContent);
|
|
@@ -551,11 +552,11 @@ fi
|
|
|
551
552
|
// Create new hook
|
|
552
553
|
const hookContent = `#!/bin/bash
|
|
553
554
|
# Codex CLI pre-session hook for workstream injection
|
|
554
|
-
# Installed by
|
|
555
|
+
# Installed by coder-config
|
|
555
556
|
|
|
556
557
|
# Check for active workstream via env var
|
|
557
|
-
if [ -n "$
|
|
558
|
-
|
|
558
|
+
if [ -n "$CODER_WORKSTREAM" ] && command -v coder-config &> /dev/null; then
|
|
559
|
+
coder-config workstream inject --silent
|
|
559
560
|
fi
|
|
560
561
|
`;
|
|
561
562
|
fs.writeFileSync(targetHookPath, hookContent);
|
|
@@ -568,7 +569,7 @@ fi
|
|
|
568
569
|
console.log('When a workstream is active, Codex will see the restriction');
|
|
569
570
|
console.log('and context at the start of each session.');
|
|
570
571
|
console.log('\nTo activate a workstream for this session:');
|
|
571
|
-
console.log(' export
|
|
572
|
+
console.log(' export CODER_WORKSTREAM=<name-or-id>');
|
|
572
573
|
|
|
573
574
|
return true;
|
|
574
575
|
}
|
|
@@ -578,9 +579,9 @@ fi
|
|
|
578
579
|
*/
|
|
579
580
|
function workstreamDeactivate() {
|
|
580
581
|
console.log('To deactivate the workstream for this session, run:');
|
|
581
|
-
console.log(' unset
|
|
582
|
+
console.log(' unset CODER_WORKSTREAM');
|
|
582
583
|
console.log('\nOr to clear the global active workstream:');
|
|
583
|
-
console.log('
|
|
584
|
+
console.log(' coder-config workstream use --clear');
|
|
584
585
|
return true;
|
|
585
586
|
}
|
|
586
587
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coder-config",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.7",
|
|
4
4
|
"description": "Configuration manager for AI coding tools - Claude Code, Gemini CLI, Codex CLI, Antigravity. Manage MCPs, rules, permissions, memory, and workstreams.",
|
|
5
5
|
"author": "regression.io",
|
|
6
6
|
"main": "config-loader.js",
|
|
@@ -3201,4 +3201,4 @@ claude-config --help # See all commands
|
|
|
3201
3201
|
Thanks for using Coder Config. We built this to make working with AI coding tools easier and more powerful.
|
|
3202
3202
|
|
|
3203
3203
|
Happy coding!
|
|
3204
|
-
`}},JH={...zH,...HH,...WH,...VH,...UH,...GH,...KH,...qH,...YH,...XH},au={welcome:{bg:"bg-violet-500",light:"bg-violet-100 dark:bg-violet-950",text:"text-violet-600 dark:text-violet-400"},"first-project":{bg:"bg-blue-500",light:"bg-blue-100 dark:bg-blue-950",text:"text-blue-600 dark:text-blue-400"},"rules-guide":{bg:"bg-emerald-500",light:"bg-emerald-100 dark:bg-emerald-950",text:"text-emerald-600 dark:text-emerald-400"},"mcp-guide":{bg:"bg-orange-500",light:"bg-orange-100 dark:bg-orange-950",text:"text-orange-600 dark:text-orange-400"},"permissions-guide":{bg:"bg-green-500",light:"bg-green-100 dark:bg-green-950",text:"text-green-600 dark:text-green-400"},"memory-guide":{bg:"bg-pink-500",light:"bg-pink-100 dark:bg-pink-950",text:"text-pink-600 dark:text-pink-400"},"plugins-guide":{bg:"bg-purple-500",light:"bg-purple-100 dark:bg-purple-950",text:"text-purple-600 dark:text-purple-400"},"workstreams-guide":{bg:"bg-cyan-500",light:"bg-cyan-100 dark:bg-cyan-950",text:"text-cyan-600 dark:text-cyan-400"},"multi-tool-guide":{bg:"bg-amber-500",light:"bg-amber-100 dark:bg-amber-950",text:"text-amber-600 dark:text-amber-400"},"next-steps":{bg:"bg-rose-500",light:"bg-rose-100 dark:bg-rose-950",text:"text-rose-600 dark:text-rose-400"}},Bw="claude-config-tutorial-visited";function QH(){const[e,t]=C.useState("intro"),[r,s]=C.useState({welcome:!0}),[o,u]=C.useState(()=>{try{return JSON.parse(localStorage.getItem(Bw)||"[]")}catch{return[]}}),d=C.useRef(null);C.useEffect(()=>{if(d.current){const y=d.current.querySelector("[data-radix-scroll-area-viewport]");y&&(y.scrollTop=0)}},[e]),C.useEffect(()=>{if(e&&!o.includes(e)){const y=[...o,e];u(y),localStorage.setItem(Bw,JSON.stringify(y))}},[e,o]);const l=y=>{s(j=>({...j,[y]:!j[y]}))},c=JH[e],f=za.flatMap(y=>y.subsections.length>0?y.subsections.map(j=>j.id):[y.id]),m=f.indexOf(e),h=m>0?f[m-1]:null,x=m<f.length-1?f[m+1]:null,w=Math.round(o.length/f.length*100),_=(()=>{for(const y of za)if(y.id===e||y.subsections.some(j=>j.id===e))return y;return za[0]})(),v=au[_.id]||au.welcome,b=y=>{for(const j of za){if(j.id===y)return j.title;const E=j.subsections.find(P=>P.id===y);if(E)return E.title}return""},g=y=>({__html:I2(y)});return n.jsxs("div",{className:"flex h-full",children:[n.jsxs("div",{className:"w-72 border-r border-border bg-muted/30 flex flex-col",children:[n.jsxs("div",{className:"p-4 border-b border-border bg-gradient-to-r from-indigo-500/10 to-purple-500/10",children:[n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx("div",{className:"w-10 h-10 rounded-xl bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg",children:n.jsx(ku,{className:"w-5 h-5 text-white"})}),n.jsxs("div",{children:[n.jsx("h2",{className:"font-semibold text-foreground",children:"Tutorial"}),n.jsx("p",{className:"text-xs text-muted-foreground",children:"Step-by-step guide"})]})]}),n.jsxs("div",{className:"mt-3",children:[n.jsxs("div",{className:"flex items-center justify-between text-xs text-muted-foreground mb-1",children:[n.jsx("span",{children:"Progress"}),n.jsxs("span",{children:[w,"% complete"]})]}),n.jsx(W2,{value:w,className:"h-2"})]})]}),n.jsx(Nn,{className:"flex-1",children:n.jsx("div",{className:"p-2",children:za.map((y,j)=>{const E=y.icon,P=au[y.id]||au.welcome,T=e===y.id||y.subsections.some(R=>R.id===e),N=y.subsections.length>0?y.subsections.every(R=>o.includes(R.id)):o.includes(y.id);return n.jsxs("div",{className:"mb-1",children:[n.jsxs("button",{className:je("w-full flex items-center gap-2 px-3 py-2.5 text-sm rounded-lg hover:bg-accent text-left transition-all",T&&"bg-accent"),onClick:()=>{y.subsections.length>0?(l(y.id),r[y.id]||t(y.subsections[0].id)):t(y.id)},children:[n.jsx("span",{className:je("w-7 h-7 rounded-lg flex items-center justify-center transition-colors",T?P.bg+" text-white":P.light),children:N?n.jsx(Em,{className:"w-4 h-4"}):n.jsx(E,{className:je("w-4 h-4",!T&&P.text)})}),n.jsx("span",{className:je("flex-1",T&&"font-medium"),children:y.title}),y.subsections.length>0&&n.jsx(Kr,{className:je("w-4 h-4 text-muted-foreground transition-transform",r[y.id]&&"rotate-90")})]}),y.subsections.length>0&&r[y.id]&&n.jsx("div",{className:"ml-4 mt-1 space-y-0.5 border-l-2 border-border pl-3",children:y.subsections.map(R=>{const I=e===R.id,O=o.includes(R.id);return n.jsxs("button",{className:je("w-full text-left px-3 py-1.5 text-sm rounded-md hover:bg-accent text-foreground flex items-center gap-2",I&&"bg-accent font-medium",I&&P.text),onClick:()=>t(R.id),children:[O&&!I&&n.jsx(Em,{className:"w-3 h-3 text-green-500"}),n.jsx("span",{className:je(!O&&!I&&"ml-5"),children:R.title})]},R.id)})})]},y.id)})})})]}),n.jsxs("div",{className:"flex-1 overflow-hidden flex flex-col",children:[n.jsx("div",{className:je("px-8 py-4 border-b border-border",v.light),children:n.jsxs("div",{className:"max-w-3xl mx-auto flex items-center gap-3",children:[n.jsx("div",{className:je("w-10 h-10 rounded-xl flex items-center justify-center",v.bg,"text-white"),children:n.jsx(_.icon,{className:"w-5 h-5"})}),n.jsxs("div",{children:[n.jsx("p",{className:je("text-sm font-medium",v.text),children:_.title}),n.jsx("h1",{className:"text-lg font-semibold text-foreground",children:(c==null?void 0:c.title)||b(e)})]})]})}),n.jsx(Nn,{className:"flex-1",ref:d,children:n.jsx("div",{className:"max-w-3xl mx-auto p-8",children:c?n.jsx("div",{className:"prose prose-sm max-w-none dark:prose-invert prose-headings:text-foreground prose-p:text-muted-foreground prose-strong:text-foreground prose-code:text-primary prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-pre:bg-muted prose-pre:border prose-pre:border-border",dangerouslySetInnerHTML:g(c.content)}):n.jsxs("div",{className:"text-center text-muted-foreground py-12",children:[n.jsx(ku,{className:"w-12 h-12 mx-auto mb-4 opacity-50"}),n.jsx("p",{children:"Select a topic from the sidebar"})]})})}),n.jsx("div",{className:"border-t border-border p-4 bg-muted/30",children:n.jsxs("div",{className:"max-w-3xl mx-auto flex items-center justify-between",children:[h?n.jsxs(le,{variant:"ghost",onClick:()=>t(h),className:"flex items-center gap-2",children:[n.jsx(cM,{className:"w-4 h-4"}),n.jsx("span",{className:"max-w-[150px] truncate",children:b(h)})]}):n.jsx("div",{}),n.jsxs("span",{className:"text-xs text-muted-foreground",children:[m+1," of ",f.length]}),x?n.jsxs(le,{onClick:()=>t(x),className:je("flex items-center gap-2",v.bg,"hover:opacity-90"),children:[n.jsx("span",{className:"max-w-[150px] truncate",children:b(x)}),n.jsx(Hp,{className:"w-4 h-4"})]}):n.jsx(le,{variant:"outline",onClick:()=>t("intro"),className:"flex items-center gap-2",children:"Start Over"})]})})]})]})}const ZH=[{id:"projects",label:"All Projects",icon:Zs,section:"Projects"},{id:"explorer",label:"Project Explorer",icon:si,section:"Projects"},{id:"registry",label:"MCP Registry",icon:ns,section:"Tools"},{id:"plugins",label:"Plugins",icon:Fi,section:"Tools"},{id:"memory",label:"Memory",icon:Ii,section:"Tools"},{id:"workstreams",label:"Workstreams",icon:P1,section:"Tools"},{id:"claude-settings",label:"Claude Code",icon:as,section:"Configuration"},{id:"gemini-settings",label:"Gemini CLI",icon:Ft,section:"Configuration"},{id:"codex-settings",label:"Codex CLI",icon:Ft,section:"Configuration",isNew:!0},{id:"antigravity-settings",label:"Antigravity",icon:wl,section:"Configuration"},{id:"create-mcp",label:"Create MCP",icon:Qn,section:"Developer"},{id:"preferences",label:"Preferences",icon:A1,section:"System"},{id:"tutorial",label:"Tutorial",icon:ku,section:"Help",isNew:!0},{id:"docs",label:"Docs & Help",icon:Li,section:"Help"}],Fw=(e,t)=>{try{const r=localStorage.getItem(`claude-config-${e}`);return r?JSON.parse(r):t}catch{return t}},eW=(e,t)=>{try{localStorage.setItem(`claude-config-${e}`,JSON.stringify(t))}catch{}};function tW(){const[e,t]=C.useState(()=>Fw("currentView","explorer")),[r,s]=C.useState(!0),[o,u]=C.useState({dir:"",subprojects:[],hierarchy:[]}),[d,l]=C.useState([]),[c,f]=C.useState({mcpServers:{}}),[m,h]=C.useState([]),[x,w]=C.useState([]),[k,_]=C.useState(null),[v,b]=C.useState(null),[g,y]=C.useState(!1),[j,E]=C.useState({title:"",type:""}),[P,T]=C.useState(""),[N,R]=C.useState(""),[I,O]=C.useState({}),[$,W]=C.useState(null),[Y,z]=C.useState(null),[K,G]=C.useState(!1),[U,L]=C.useState([]),[M,B]=C.useState(null),[q,F]=C.useState(!1),[oe,fe]=C.useState(null);C.useEffect(()=>{eW("currentView",e)},[e]);const me=C.useCallback(async()=>{try{const[re,de,De,Re,Ze]=await Promise.all([ke.getProject(),ke.getConfigs(),ke.getRegistry(),ke.getRules(),ke.getCommands()]);u(re),l(de),f(De),h(Re),w(Ze),de.length>0&&!k&&_(de[de.length-1])}catch(re){ee.error("Failed to load data: "+re.message)}finally{s(!1)}},[k]),X=C.useCallback(async(re=!1)=>{var de;try{const De=await ke.getProjects();L(De.projects||[]);const Re=(de=De.projects)==null?void 0:de.find(Ze=>Ze.isActive);if(B(Re||null),re&&Re){const Ze=await ke.getSubprojects(Re.path);fe({dir:Re.path,subprojects:Ze.subprojects||[]})}re&&!Re&&(Fw("currentView",null)||t("projects"))}catch{console.log("Projects API not available")}},[]),H=async re=>{try{const de=await ke.setActiveProject(re);de.success?(u({dir:de.dir,hierarchy:de.hierarchy,subprojects:de.subprojects}),fe({dir:de.dir,subprojects:de.subprojects}),B(de.project),L(De=>De.map(Re=>({...Re,isActive:Re.id===re}))),await me(),ee.success(`Switched to ${de.project.name}`)):ee.error(de.error||"Failed to switch project")}catch(de){ee.error("Failed to switch project: "+de.message)}},ae=re=>{L(de=>[...de,{...re,exists:!0,hasClaudeConfig:!1}])};C.useEffect(()=>{me(),X(!0),ke.checkVersion().then(re=>{W(re==null?void 0:re.installedVersion),re!=null&&re.updateAvailable&&(re==null?void 0:re.updateMethod)==="npm"&&z(re)}).catch(()=>{})},[]);const ne=async()=>{if(Y!=null&&Y.updateAvailable){G(!0);try{const re=await ke.performUpdate({updateMethod:Y.updateMethod,sourcePath:Y.sourcePath,targetVersion:Y.latestVersion});re.success?(ee.success(`Updated to v${re.newVersion}! Reloading...`),setTimeout(()=>window.location.reload(),1500)):(ee.error("Update failed: "+re.error),G(!1))}catch(re){ee.error("Update failed: "+re.message),G(!1)}}};C.useEffect(()=>{const re=async()=>{try{const{hashes:De}=await ke.getFileHashes(),Re=I;(Object.keys(De).some(Se=>Re[Se]!==De[Se])||Object.keys(Re).some(Se=>!De[Se]))&&Object.keys(Re).length>0&&(ee.info("Files changed externally, reloading..."),await me()),O(De)}catch{}},de=setInterval(re,2e3);return re(),()=>clearInterval(de)},[I,me]);const J=new Set;d.forEach(re=>{var de,De;(((de=re.config)==null?void 0:de.include)||[]).forEach(Re=>J.add(Re)),Object.keys(((De=re.config)==null?void 0:De.mcpServers)||{}).forEach(Re=>J.add(Re))});const ie={mcps:J.size,rules:m.length,commands:x.length},ye=async()=>{try{const re=await ke.applyConfig(o.dir);if(re.tools){const de=Object.entries(re.tools).filter(([,De])=>De).map(([De])=>De==="claude"?"Claude Code":"Antigravity");de.length>0?ee.success(`Config applied to: ${de.join(", ")}`):ee.warning("No tools were updated")}else ee.success("Configuration applied successfully!")}catch(re){ee.error("Failed to apply config: "+re.message)}},Oe=async()=>{s(!0),await me(),ee.success("Data refreshed")};if(r)return n.jsx("div",{className:"min-h-screen bg-background flex items-center justify-center",children:n.jsxs("div",{className:"flex flex-col items-center gap-4",children:[n.jsx(ut,{className:"w-8 h-8 animate-spin text-primary"}),n.jsx("p",{className:"text-muted-foreground",children:"Loading configuration..."})]})});const Ve=()=>{switch(e){case"explorer":return n.jsx(GF,{project:o,onRefresh:me});case"registry":return n.jsx(lH,{registry:c,searchQuery:P,setSearchQuery:T,onUpdate:me});case"plugins":return n.jsx(PH,{});case"memory":return n.jsx(cH,{project:o,onUpdate:me});case"create-mcp":return n.jsx(Fz,{project:o});case"claude-settings":return n.jsx(Cz,{});case"gemini-settings":return n.jsx(kz,{});case"codex-settings":return n.jsx(Oz,{});case"antigravity-settings":return n.jsx(Az,{});case"preferences":return n.jsx(Sz,{});case"projects":return n.jsx(uH,{onProjectSwitch:re=>{u({dir:re.dir,hierarchy:re.hierarchy,subprojects:re.subprojects}),fe({dir:re.dir,subprojects:re.subprojects}),me(),X()}});case"workstreams":return n.jsx(AH,{projects:U,onWorkstreamChange:re=>{ee.success(`Switched to workstream: ${re.name}`)}});case"docs":return n.jsx(NH,{});case"tutorial":return n.jsx(QH,{});default:return null}};return n.jsxs("div",{className:"min-h-screen bg-background",children:[n.jsx("header",{className:"h-16 bg-card border-b border-border sticky top-0 z-50 shadow-sm",children:n.jsxs("div",{className:"h-full px-6 flex items-center justify-between",children:[n.jsxs("div",{className:"flex items-center gap-4",children:[n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx("div",{className:"w-10 h-10 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg shadow-indigo-500/20",children:n.jsx(kn,{className:"w-5 h-5 text-white"})}),n.jsx("div",{children:n.jsxs("h1",{className:"text-xl font-bold text-foreground",children:["Claude ",n.jsx("span",{className:"text-primary",children:"Config"})]})}),Y&&n.jsxs("button",{onClick:ne,disabled:K,className:"ml-3 px-2.5 py-1 text-xs font-medium bg-green-100 text-green-700 hover:bg-green-200 rounded-full flex items-center gap-1.5 transition-colors disabled:opacity-50",children:[K?n.jsx(ut,{className:"w-3 h-3 animate-spin"}):n.jsx(bl,{className:"w-3 h-3"}),K?"Updating...":`Update to v${Y.latestVersion}`]})]}),n.jsx(Oj,{orientation:"vertical",className:"h-6"}),n.jsx(KF,{projects:U,activeProject:M,onSwitch:H,onAddClick:()=>F(!0),onManageClick:()=>t("projects")})]}),n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx(JF,{}),n.jsx(le,{variant:"ghost",size:"sm",onClick:Oe,children:n.jsx(pr,{className:"w-4 h-4"})}),n.jsxs(le,{onClick:ye,variant:"ghost",size:"sm",className:"gap-2 text-muted-foreground hover:text-foreground",title:"Config auto-applies on save. Click to manually re-apply.",children:[n.jsx(wl,{className:"w-4 h-4"}),"Re-apply"]})]})]})}),n.jsxs("div",{className:"flex",children:[n.jsxs("aside",{className:"w-64 h-[calc(100vh-64px)] border-r border-border bg-card sticky top-16 flex flex-col",children:[n.jsx(Nn,{className:"flex-1 py-4",children:["Projects","Tools","Configuration","Developer","System","Help"].map(re=>n.jsxs("div",{className:"mb-6",children:[n.jsx("h3",{className:"px-4 mb-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground",children:re}),n.jsx("div",{className:"space-y-0.5",children:ZH.filter(de=>de.section===re).map(de=>{const De=de.icon,Re=e===de.id;return n.jsxs("button",{onClick:()=>t(de.id),className:`w-full flex items-center gap-3 px-4 py-2.5 text-sm transition-all duration-200 border-l-2 ${Re?"bg-accent border-primary text-primary font-medium":"border-transparent text-muted-foreground hover:text-foreground hover:bg-accent"}`,children:[n.jsx(De,{className:"w-4 h-4"}),n.jsx("span",{className:"flex-1 text-left",children:de.label}),de.badge&&n.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full font-medium ${Re?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground"}`,children:ie[de.badge]}),de.isNew&&n.jsx("span",{className:"text-xs px-1.5 py-0.5 rounded font-medium bg-green-500/20 text-green-600 dark:text-green-400",children:"new"})]},de.id)})})]},re))}),n.jsx("div",{className:"px-4 py-3 border-t border-border",children:n.jsx("span",{className:"text-xs text-muted-foreground",children:$?`v${$}`:""})})]}),n.jsx("main",{className:je("flex-1 overflow-auto",["explorer","docs","tutorial"].includes(e)?"h-[calc(100vh-64px)]":"p-6"),children:n.jsx(Vw,{mode:"wait",children:n.jsx(Eo.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},exit:{opacity:0,y:-10},transition:{duration:.2},className:["explorer","docs","tutorial"].includes(e)?"h-full":"",children:Ve()},e)})})]}),n.jsx(Mj,{open:q,onOpenChange:F,onAdded:ae}),n.jsx(qF,{onStartTutorial:()=>t("tutorial")})]})}function rW(){const[e,t]=C.useState(!1),[r,s]=C.useState(!1),[o,u]=C.useState(!1),d=C.useCallback(async()=>{s(!0);try{await ke.restartServer(),ee.info("Server restarting... page will reload."),setTimeout(()=>{window.location.reload()},2e3)}catch(c){ee.error("Failed to restart: "+c.message),s(!1)}},[]),l=C.useCallback(()=>{u(!0),t(!1),ee.dismiss("update-available")},[]);return C.useEffect(()=>{let c;const f=async()=>{try{const m=await ke.getVersion();console.log("[Update Check]",m),m.needsRestart&&!o&&t(!0)}catch{}};return f(),c=setInterval(f,3e4),()=>clearInterval(c)},[o]),n.jsx(YF,{children:n.jsxs("div",{className:"min-h-screen bg-background",children:[e&&!r&&n.jsxs("div",{className:"fixed top-0 left-0 right-0 z-50 bg-blue-600 text-white px-4 py-2 flex items-center justify-center gap-2 shadow-lg cursor-pointer hover:bg-blue-700 transition-colors",onClick:d,children:[n.jsx(pr,{className:"w-4 h-4"}),n.jsx("span",{className:"text-sm font-medium",children:"Update available — click to restart"}),n.jsx("button",{onClick:c=>{c.stopPropagation(),l()},className:"absolute right-3 p-1 hover:bg-blue-500 rounded",children:n.jsx(ei,{className:"w-4 h-4"})})]}),r&&n.jsxs("div",{className:"fixed top-0 left-0 right-0 z-50 bg-amber-600 text-white px-4 py-2 flex items-center justify-center gap-2",children:[n.jsx(pr,{className:"w-4 h-4 animate-spin"}),n.jsx("span",{className:"text-sm font-medium",children:"Restarting server..."})]}),n.jsx(tW,{}),n.jsx(yP,{position:"bottom-right",richColors:!0})]})})}GE.createRoot(document.getElementById("root")).render(n.jsx(rW,{}));
|
|
3204
|
+
`}},JH={...zH,...HH,...WH,...VH,...UH,...GH,...KH,...qH,...YH,...XH},au={welcome:{bg:"bg-violet-500",light:"bg-violet-100 dark:bg-violet-950",text:"text-violet-600 dark:text-violet-400"},"first-project":{bg:"bg-blue-500",light:"bg-blue-100 dark:bg-blue-950",text:"text-blue-600 dark:text-blue-400"},"rules-guide":{bg:"bg-emerald-500",light:"bg-emerald-100 dark:bg-emerald-950",text:"text-emerald-600 dark:text-emerald-400"},"mcp-guide":{bg:"bg-orange-500",light:"bg-orange-100 dark:bg-orange-950",text:"text-orange-600 dark:text-orange-400"},"permissions-guide":{bg:"bg-green-500",light:"bg-green-100 dark:bg-green-950",text:"text-green-600 dark:text-green-400"},"memory-guide":{bg:"bg-pink-500",light:"bg-pink-100 dark:bg-pink-950",text:"text-pink-600 dark:text-pink-400"},"plugins-guide":{bg:"bg-purple-500",light:"bg-purple-100 dark:bg-purple-950",text:"text-purple-600 dark:text-purple-400"},"workstreams-guide":{bg:"bg-cyan-500",light:"bg-cyan-100 dark:bg-cyan-950",text:"text-cyan-600 dark:text-cyan-400"},"multi-tool-guide":{bg:"bg-amber-500",light:"bg-amber-100 dark:bg-amber-950",text:"text-amber-600 dark:text-amber-400"},"next-steps":{bg:"bg-rose-500",light:"bg-rose-100 dark:bg-rose-950",text:"text-rose-600 dark:text-rose-400"}},Bw="claude-config-tutorial-visited";function QH(){const[e,t]=C.useState("intro"),[r,s]=C.useState({welcome:!0}),[o,u]=C.useState(()=>{try{return JSON.parse(localStorage.getItem(Bw)||"[]")}catch{return[]}}),d=C.useRef(null);C.useEffect(()=>{if(d.current){const y=d.current.querySelector("[data-radix-scroll-area-viewport]");y&&(y.scrollTop=0)}},[e]),C.useEffect(()=>{if(e&&!o.includes(e)){const y=[...o,e];u(y),localStorage.setItem(Bw,JSON.stringify(y))}},[e,o]);const l=y=>{s(j=>({...j,[y]:!j[y]}))},c=JH[e],f=za.flatMap(y=>y.subsections.length>0?y.subsections.map(j=>j.id):[y.id]),m=f.indexOf(e),h=m>0?f[m-1]:null,x=m<f.length-1?f[m+1]:null,w=Math.round(o.length/f.length*100),_=(()=>{for(const y of za)if(y.id===e||y.subsections.some(j=>j.id===e))return y;return za[0]})(),v=au[_.id]||au.welcome,b=y=>{for(const j of za){if(j.id===y)return j.title;const E=j.subsections.find(P=>P.id===y);if(E)return E.title}return""},g=y=>({__html:I2(y)});return n.jsxs("div",{className:"flex h-full",children:[n.jsxs("div",{className:"w-72 border-r border-border bg-muted/30 flex flex-col",children:[n.jsxs("div",{className:"p-4 border-b border-border bg-gradient-to-r from-indigo-500/10 to-purple-500/10",children:[n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx("div",{className:"w-10 h-10 rounded-xl bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg",children:n.jsx(ku,{className:"w-5 h-5 text-white"})}),n.jsxs("div",{children:[n.jsx("h2",{className:"font-semibold text-foreground",children:"Tutorial"}),n.jsx("p",{className:"text-xs text-muted-foreground",children:"Step-by-step guide"})]})]}),n.jsxs("div",{className:"mt-3",children:[n.jsxs("div",{className:"flex items-center justify-between text-xs text-muted-foreground mb-1",children:[n.jsx("span",{children:"Progress"}),n.jsxs("span",{children:[w,"% complete"]})]}),n.jsx(W2,{value:w,className:"h-2"})]})]}),n.jsx(Nn,{className:"flex-1",children:n.jsx("div",{className:"p-2",children:za.map((y,j)=>{const E=y.icon,P=au[y.id]||au.welcome,T=e===y.id||y.subsections.some(R=>R.id===e),N=y.subsections.length>0?y.subsections.every(R=>o.includes(R.id)):o.includes(y.id);return n.jsxs("div",{className:"mb-1",children:[n.jsxs("button",{className:je("w-full flex items-center gap-2 px-3 py-2.5 text-sm rounded-lg hover:bg-accent text-left transition-all",T&&"bg-accent"),onClick:()=>{y.subsections.length>0?(l(y.id),r[y.id]||t(y.subsections[0].id)):t(y.id)},children:[n.jsx("span",{className:je("w-7 h-7 rounded-lg flex items-center justify-center transition-colors",T?P.bg+" text-white":P.light),children:N?n.jsx(Em,{className:"w-4 h-4"}):n.jsx(E,{className:je("w-4 h-4",!T&&P.text)})}),n.jsx("span",{className:je("flex-1",T&&"font-medium"),children:y.title}),y.subsections.length>0&&n.jsx(Kr,{className:je("w-4 h-4 text-muted-foreground transition-transform",r[y.id]&&"rotate-90")})]}),y.subsections.length>0&&r[y.id]&&n.jsx("div",{className:"ml-4 mt-1 space-y-0.5 border-l-2 border-border pl-3",children:y.subsections.map(R=>{const I=e===R.id,O=o.includes(R.id);return n.jsxs("button",{className:je("w-full text-left px-3 py-1.5 text-sm rounded-md hover:bg-accent text-foreground flex items-center gap-2",I&&"bg-accent font-medium",I&&P.text),onClick:()=>t(R.id),children:[O&&!I&&n.jsx(Em,{className:"w-3 h-3 text-green-500"}),n.jsx("span",{className:je(!O&&!I&&"ml-5"),children:R.title})]},R.id)})})]},y.id)})})})]}),n.jsxs("div",{className:"flex-1 overflow-hidden flex flex-col",children:[n.jsx("div",{className:je("px-8 py-4 border-b border-border",v.light),children:n.jsxs("div",{className:"max-w-3xl mx-auto flex items-center gap-3",children:[n.jsx("div",{className:je("w-10 h-10 rounded-xl flex items-center justify-center",v.bg,"text-white"),children:n.jsx(_.icon,{className:"w-5 h-5"})}),n.jsxs("div",{children:[n.jsx("p",{className:je("text-sm font-medium",v.text),children:_.title}),n.jsx("h1",{className:"text-lg font-semibold text-foreground",children:(c==null?void 0:c.title)||b(e)})]})]})}),n.jsx(Nn,{className:"flex-1",ref:d,children:n.jsx("div",{className:"max-w-3xl mx-auto p-8",children:c?n.jsx("div",{className:"prose prose-sm max-w-none dark:prose-invert prose-headings:text-foreground prose-p:text-muted-foreground prose-strong:text-foreground prose-code:text-primary prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-pre:bg-muted prose-pre:border prose-pre:border-border",dangerouslySetInnerHTML:g(c.content)}):n.jsxs("div",{className:"text-center text-muted-foreground py-12",children:[n.jsx(ku,{className:"w-12 h-12 mx-auto mb-4 opacity-50"}),n.jsx("p",{children:"Select a topic from the sidebar"})]})})}),n.jsx("div",{className:"border-t border-border p-4 bg-muted/30",children:n.jsxs("div",{className:"max-w-3xl mx-auto flex items-center justify-between",children:[h?n.jsxs(le,{variant:"ghost",onClick:()=>t(h),className:"flex items-center gap-2",children:[n.jsx(cM,{className:"w-4 h-4"}),n.jsx("span",{className:"max-w-[150px] truncate",children:b(h)})]}):n.jsx("div",{}),n.jsxs("span",{className:"text-xs text-muted-foreground",children:[m+1," of ",f.length]}),x?n.jsxs(le,{onClick:()=>t(x),className:je("flex items-center gap-2",v.bg,"hover:opacity-90"),children:[n.jsx("span",{className:"max-w-[150px] truncate",children:b(x)}),n.jsx(Hp,{className:"w-4 h-4"})]}):n.jsx(le,{variant:"outline",onClick:()=>t("intro"),className:"flex items-center gap-2",children:"Start Over"})]})})]})]})}const ZH=[{id:"projects",label:"All Projects",icon:Zs,section:"Projects"},{id:"explorer",label:"Project Explorer",icon:si,section:"Projects"},{id:"registry",label:"MCP Registry",icon:ns,section:"Tools"},{id:"plugins",label:"Plugins",icon:Fi,section:"Tools"},{id:"memory",label:"Memory",icon:Ii,section:"Tools"},{id:"workstreams",label:"Workstreams",icon:P1,section:"Tools"},{id:"claude-settings",label:"Claude Code",icon:as,section:"Configuration"},{id:"gemini-settings",label:"Gemini CLI",icon:Ft,section:"Configuration"},{id:"codex-settings",label:"Codex CLI",icon:Ft,section:"Configuration",isNew:!0},{id:"antigravity-settings",label:"Antigravity",icon:wl,section:"Configuration"},{id:"create-mcp",label:"Create MCP",icon:Qn,section:"Developer"},{id:"preferences",label:"Preferences",icon:A1,section:"System"},{id:"tutorial",label:"Tutorial",icon:ku,section:"Help",isNew:!0},{id:"docs",label:"Docs & Help",icon:Li,section:"Help"}],Fw=(e,t)=>{try{const r=localStorage.getItem(`claude-config-${e}`);return r?JSON.parse(r):t}catch{return t}},eW=(e,t)=>{try{localStorage.setItem(`claude-config-${e}`,JSON.stringify(t))}catch{}};function tW(){const[e,t]=C.useState(()=>Fw("currentView","explorer")),[r,s]=C.useState(!0),[o,u]=C.useState({dir:"",subprojects:[],hierarchy:[]}),[d,l]=C.useState([]),[c,f]=C.useState({mcpServers:{}}),[m,h]=C.useState([]),[x,w]=C.useState([]),[k,_]=C.useState(null),[v,b]=C.useState(null),[g,y]=C.useState(!1),[j,E]=C.useState({title:"",type:""}),[P,T]=C.useState(""),[N,R]=C.useState(""),[I,O]=C.useState({}),[$,W]=C.useState(null),[Y,z]=C.useState(null),[K,G]=C.useState(!1),[U,L]=C.useState([]),[M,B]=C.useState(null),[q,F]=C.useState(!1),[oe,fe]=C.useState(null);C.useEffect(()=>{eW("currentView",e)},[e]);const me=C.useCallback(async()=>{try{const[re,de,De,Re,Ze]=await Promise.all([ke.getProject(),ke.getConfigs(),ke.getRegistry(),ke.getRules(),ke.getCommands()]);u(re),l(de),f(De),h(Re),w(Ze),de.length>0&&!k&&_(de[de.length-1])}catch(re){ee.error("Failed to load data: "+re.message)}finally{s(!1)}},[k]),X=C.useCallback(async(re=!1)=>{var de;try{const De=await ke.getProjects();L(De.projects||[]);const Re=(de=De.projects)==null?void 0:de.find(Ze=>Ze.isActive);if(B(Re||null),re&&Re){const Ze=await ke.getSubprojects(Re.path);fe({dir:Re.path,subprojects:Ze.subprojects||[]})}re&&!Re&&(Fw("currentView",null)||t("projects"))}catch{console.log("Projects API not available")}},[]),H=async re=>{try{const de=await ke.setActiveProject(re);de.success?(u({dir:de.dir,hierarchy:de.hierarchy,subprojects:de.subprojects}),fe({dir:de.dir,subprojects:de.subprojects}),B(de.project),L(De=>De.map(Re=>({...Re,isActive:Re.id===re}))),await me(),ee.success(`Switched to ${de.project.name}`)):ee.error(de.error||"Failed to switch project")}catch(de){ee.error("Failed to switch project: "+de.message)}},ae=re=>{L(de=>[...de,{...re,exists:!0,hasClaudeConfig:!1}])};C.useEffect(()=>{me(),X(!0),ke.checkVersion().then(re=>{W(re==null?void 0:re.installedVersion),re!=null&&re.updateAvailable&&(re==null?void 0:re.updateMethod)==="npm"&&z(re)}).catch(()=>{})},[]);const ne=async()=>{if(Y!=null&&Y.updateAvailable){G(!0);try{const re=await ke.performUpdate({updateMethod:Y.updateMethod,sourcePath:Y.sourcePath,targetVersion:Y.latestVersion});re.success?(ee.success(`Updated to v${re.newVersion}! Reloading...`),setTimeout(()=>window.location.reload(),1500)):(ee.error("Update failed: "+re.error),G(!1))}catch(re){ee.error("Update failed: "+re.message),G(!1)}}};C.useEffect(()=>{const re=async()=>{try{const{hashes:De}=await ke.getFileHashes(),Re=I;(Object.keys(De).some(Se=>Re[Se]!==De[Se])||Object.keys(Re).some(Se=>!De[Se]))&&Object.keys(Re).length>0&&(ee.info("Files changed externally, reloading..."),await me()),O(De)}catch{}},de=setInterval(re,2e3);return re(),()=>clearInterval(de)},[I,me]);const J=new Set;d.forEach(re=>{var de,De;(((de=re.config)==null?void 0:de.include)||[]).forEach(Re=>J.add(Re)),Object.keys(((De=re.config)==null?void 0:De.mcpServers)||{}).forEach(Re=>J.add(Re))});const ie={mcps:J.size,rules:m.length,commands:x.length},ye=async()=>{try{const re=await ke.applyConfig(o.dir);if(re.tools){const de=Object.entries(re.tools).filter(([,De])=>De).map(([De])=>De==="claude"?"Claude Code":"Antigravity");de.length>0?ee.success(`Config applied to: ${de.join(", ")}`):ee.warning("No tools were updated")}else ee.success("Configuration applied successfully!")}catch(re){ee.error("Failed to apply config: "+re.message)}},Oe=async()=>{s(!0),await me(),ee.success("Data refreshed")};if(r)return n.jsx("div",{className:"min-h-screen bg-background flex items-center justify-center",children:n.jsxs("div",{className:"flex flex-col items-center gap-4",children:[n.jsx(ut,{className:"w-8 h-8 animate-spin text-primary"}),n.jsx("p",{className:"text-muted-foreground",children:"Loading configuration..."})]})});const Ve=()=>{switch(e){case"explorer":return n.jsx(GF,{project:o,onRefresh:me});case"registry":return n.jsx(lH,{registry:c,searchQuery:P,setSearchQuery:T,onUpdate:me});case"plugins":return n.jsx(PH,{});case"memory":return n.jsx(cH,{project:o,onUpdate:me});case"create-mcp":return n.jsx(Fz,{project:o});case"claude-settings":return n.jsx(Cz,{});case"gemini-settings":return n.jsx(kz,{});case"codex-settings":return n.jsx(Oz,{});case"antigravity-settings":return n.jsx(Az,{});case"preferences":return n.jsx(Sz,{});case"projects":return n.jsx(uH,{onProjectSwitch:re=>{u({dir:re.dir,hierarchy:re.hierarchy,subprojects:re.subprojects}),fe({dir:re.dir,subprojects:re.subprojects}),me(),X()}});case"workstreams":return n.jsx(AH,{projects:U,onWorkstreamChange:re=>{ee.success(`Switched to workstream: ${re.name}`)}});case"docs":return n.jsx(NH,{});case"tutorial":return n.jsx(QH,{});default:return null}};return n.jsxs("div",{className:"min-h-screen bg-background",children:[n.jsx("header",{className:"h-16 bg-card border-b border-border sticky top-0 z-50 shadow-sm",children:n.jsxs("div",{className:"h-full px-6 flex items-center justify-between",children:[n.jsxs("div",{className:"flex items-center gap-4",children:[n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx("div",{className:"w-10 h-10 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg shadow-indigo-500/20",children:n.jsx(kn,{className:"w-5 h-5 text-white"})}),n.jsx("div",{children:n.jsxs("h1",{className:"text-xl font-bold text-foreground",children:["Coder ",n.jsx("span",{className:"text-primary",children:"Config"})]})}),Y&&n.jsxs("button",{onClick:ne,disabled:K,className:"ml-3 px-2.5 py-1 text-xs font-medium bg-green-100 text-green-700 hover:bg-green-200 rounded-full flex items-center gap-1.5 transition-colors disabled:opacity-50",children:[K?n.jsx(ut,{className:"w-3 h-3 animate-spin"}):n.jsx(bl,{className:"w-3 h-3"}),K?"Updating...":`Update to v${Y.latestVersion}`]})]}),n.jsx(Oj,{orientation:"vertical",className:"h-6"}),n.jsx(KF,{projects:U,activeProject:M,onSwitch:H,onAddClick:()=>F(!0),onManageClick:()=>t("projects")})]}),n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx(JF,{}),n.jsx(le,{variant:"ghost",size:"sm",onClick:Oe,children:n.jsx(pr,{className:"w-4 h-4"})}),n.jsxs(le,{onClick:ye,variant:"ghost",size:"sm",className:"gap-2 text-muted-foreground hover:text-foreground",title:"Config auto-applies on save. Click to manually re-apply.",children:[n.jsx(wl,{className:"w-4 h-4"}),"Re-apply"]})]})]})}),n.jsxs("div",{className:"flex",children:[n.jsxs("aside",{className:"w-64 h-[calc(100vh-64px)] border-r border-border bg-card sticky top-16 flex flex-col",children:[n.jsx(Nn,{className:"flex-1 py-4",children:["Projects","Tools","Configuration","Developer","System","Help"].map(re=>n.jsxs("div",{className:"mb-6",children:[n.jsx("h3",{className:"px-4 mb-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground",children:re}),n.jsx("div",{className:"space-y-0.5",children:ZH.filter(de=>de.section===re).map(de=>{const De=de.icon,Re=e===de.id;return n.jsxs("button",{onClick:()=>t(de.id),className:`w-full flex items-center gap-3 px-4 py-2.5 text-sm transition-all duration-200 border-l-2 ${Re?"bg-accent border-primary text-primary font-medium":"border-transparent text-muted-foreground hover:text-foreground hover:bg-accent"}`,children:[n.jsx(De,{className:"w-4 h-4"}),n.jsx("span",{className:"flex-1 text-left",children:de.label}),de.badge&&n.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full font-medium ${Re?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground"}`,children:ie[de.badge]}),de.isNew&&n.jsx("span",{className:"text-xs px-1.5 py-0.5 rounded font-medium bg-green-500/20 text-green-600 dark:text-green-400",children:"new"})]},de.id)})})]},re))}),n.jsx("div",{className:"px-4 py-3 border-t border-border",children:n.jsx("span",{className:"text-xs text-muted-foreground",children:$?`v${$}`:""})})]}),n.jsx("main",{className:je("flex-1 overflow-auto",["explorer","docs","tutorial"].includes(e)?"h-[calc(100vh-64px)]":"p-6"),children:n.jsx(Vw,{mode:"wait",children:n.jsx(Eo.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},exit:{opacity:0,y:-10},transition:{duration:.2},className:["explorer","docs","tutorial"].includes(e)?"h-full":"",children:Ve()},e)})})]}),n.jsx(Mj,{open:q,onOpenChange:F,onAdded:ae}),n.jsx(qF,{onStartTutorial:()=>t("tutorial")})]})}function rW(){const[e,t]=C.useState(!1),[r,s]=C.useState(!1),[o,u]=C.useState(!1),d=C.useCallback(async()=>{s(!0);try{await ke.restartServer(),ee.info("Server restarting... page will reload."),setTimeout(()=>{window.location.reload()},2e3)}catch(c){ee.error("Failed to restart: "+c.message),s(!1)}},[]),l=C.useCallback(()=>{u(!0),t(!1),ee.dismiss("update-available")},[]);return C.useEffect(()=>{let c;const f=async()=>{try{const m=await ke.getVersion();console.log("[Update Check]",m),m.needsRestart&&!o&&t(!0)}catch{}};return f(),c=setInterval(f,3e4),()=>clearInterval(c)},[o]),n.jsx(YF,{children:n.jsxs("div",{className:"min-h-screen bg-background",children:[e&&!r&&n.jsxs("div",{className:"fixed top-0 left-0 right-0 z-50 bg-blue-600 text-white px-4 py-2 flex items-center justify-center gap-2 shadow-lg cursor-pointer hover:bg-blue-700 transition-colors",onClick:d,children:[n.jsx(pr,{className:"w-4 h-4"}),n.jsx("span",{className:"text-sm font-medium",children:"Update available — click to restart"}),n.jsx("button",{onClick:c=>{c.stopPropagation(),l()},className:"absolute right-3 p-1 hover:bg-blue-500 rounded",children:n.jsx(ei,{className:"w-4 h-4"})})]}),r&&n.jsxs("div",{className:"fixed top-0 left-0 right-0 z-50 bg-amber-600 text-white px-4 py-2 flex items-center justify-center gap-2",children:[n.jsx(pr,{className:"w-4 h-4 animate-spin"}),n.jsx("span",{className:"text-sm font-medium",children:"Restarting server..."})]}),n.jsx(tW,{}),n.jsx(yP,{position:"bottom-right",richColors:!0})]})})}GE.createRoot(document.getElementById("root")).render(n.jsx(rW,{}));
|
package/ui/dist/index.html
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
<!-- PWA Manifest -->
|
|
21
21
|
<link rel="manifest" href="/manifest.json">
|
|
22
|
-
<script type="module" crossorigin src="/assets/index-
|
|
22
|
+
<script type="module" crossorigin src="/assets/index-CTytYK0Q.js"></script>
|
|
23
23
|
<link rel="stylesheet" crossorigin href="/assets/index-DjLdm3Mr.css">
|
|
24
24
|
</head>
|
|
25
25
|
<body>
|
package/ui/routes/updates.js
CHANGED
|
@@ -41,7 +41,7 @@ function getVersionFromFile(filePath) {
|
|
|
41
41
|
*/
|
|
42
42
|
function fetchNpmVersion() {
|
|
43
43
|
return new Promise((resolve) => {
|
|
44
|
-
const url = 'https://registry.npmjs.org
|
|
44
|
+
const url = 'https://registry.npmjs.org/coder-config/latest';
|
|
45
45
|
const req = https.get(url, (res) => {
|
|
46
46
|
let data = '';
|
|
47
47
|
res.on('data', chunk => data += chunk);
|
|
@@ -167,7 +167,7 @@ async function performNpmUpdate(targetVersion) {
|
|
|
167
167
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
168
168
|
try {
|
|
169
169
|
// Use npm install @latest instead of npm update for reliable updates
|
|
170
|
-
execSync('npm install -g
|
|
170
|
+
execSync('npm install -g coder-config@latest', {
|
|
171
171
|
stdio: 'pipe',
|
|
172
172
|
timeout: 120000
|
|
173
173
|
});
|
package/ui/routes/workstreams.js
CHANGED
|
@@ -170,9 +170,12 @@ function installWorkstreamHook() {
|
|
|
170
170
|
const hookPath = path.join(hookDir, 'pre-prompt.sh');
|
|
171
171
|
|
|
172
172
|
const hookCode = `
|
|
173
|
-
# Workstream rule injection (added by
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
# Workstream rule injection (added by coder-config)
|
|
174
|
+
# Supports both CODER_WORKSTREAM (preferred) and CLAUDE_WORKSTREAM (legacy)
|
|
175
|
+
if [ -n "$CODER_WORKSTREAM" ] || [ -n "$CLAUDE_WORKSTREAM" ]; then
|
|
176
|
+
if command -v coder-config &> /dev/null; then
|
|
177
|
+
coder-config workstream inject --silent
|
|
178
|
+
fi
|
|
176
179
|
fi
|
|
177
180
|
`;
|
|
178
181
|
|