@regression-io/claude-config 0.14.16
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 +286 -0
- package/cli.js +260 -0
- package/config-loader.js +1556 -0
- package/package.json +62 -0
- package/scripts/postinstall.js +50 -0
- package/scripts/sync-version.js +65 -0
- package/shared/mcp-registry.json +117 -0
- package/templates/composites/fastapi-react-js/rules/backend-python.md +54 -0
- package/templates/composites/fastapi-react-js/rules/frontend-react.md +69 -0
- package/templates/composites/fastapi-react-js/rules/monorepo.md +77 -0
- package/templates/composites/fastapi-react-js/template.json +7 -0
- package/templates/composites/fastapi-react-ts/rules/backend-python.md +54 -0
- package/templates/composites/fastapi-react-ts/rules/frontend-react.md +64 -0
- package/templates/composites/fastapi-react-ts/rules/monorepo.md +82 -0
- package/templates/composites/fastapi-react-ts/template.json +7 -0
- package/templates/frameworks/fastapi/rules/dependencies.md +89 -0
- package/templates/frameworks/fastapi/rules/endpoints.md +86 -0
- package/templates/frameworks/fastapi/rules/errors.md +101 -0
- package/templates/frameworks/fastapi/rules/structure.md +97 -0
- package/templates/frameworks/fastapi/template.json +6 -0
- package/templates/frameworks/mcp-python/rules/resources.md +93 -0
- package/templates/frameworks/mcp-python/rules/structure.md +74 -0
- package/templates/frameworks/mcp-python/rules/tools.md +80 -0
- package/templates/frameworks/mcp-python/template.json +6 -0
- package/templates/frameworks/python-cli/rules/commands.md +103 -0
- package/templates/frameworks/python-cli/rules/output.md +107 -0
- package/templates/frameworks/python-cli/rules/structure.md +91 -0
- package/templates/frameworks/python-cli/template.json +6 -0
- package/templates/frameworks/react-js/rules/components.md +84 -0
- package/templates/frameworks/react-js/rules/hooks.md +98 -0
- package/templates/frameworks/react-js/template.json +6 -0
- package/templates/frameworks/react-ts/rules/components.md +72 -0
- package/templates/frameworks/react-ts/rules/hooks.md +87 -0
- package/templates/frameworks/react-ts/rules/state.md +93 -0
- package/templates/frameworks/react-ts/template.json +6 -0
- package/templates/languages/javascript/rules/patterns.md +126 -0
- package/templates/languages/javascript/rules/style.md +92 -0
- package/templates/languages/javascript/template.json +6 -0
- package/templates/languages/python/rules/dependencies.md +77 -0
- package/templates/languages/python/rules/patterns.md +95 -0
- package/templates/languages/python/rules/style.md +63 -0
- package/templates/languages/python/template.json +6 -0
- package/templates/languages/typescript/rules/config.md +95 -0
- package/templates/languages/typescript/rules/patterns.md +119 -0
- package/templates/languages/typescript/rules/style.md +82 -0
- package/templates/languages/typescript/template.json +6 -0
- package/templates/universal/commands/commit.md +53 -0
- package/templates/universal/commands/debug.md +53 -0
- package/templates/universal/commands/document.md +54 -0
- package/templates/universal/commands/review.md +45 -0
- package/templates/universal/commands/security-review.md +52 -0
- package/templates/universal/commands/test.md +46 -0
- package/templates/universal/rules/api-design.md +38 -0
- package/templates/universal/rules/code-quality.md +40 -0
- package/templates/universal/rules/documentation.md +38 -0
- package/templates/universal/rules/error-handling.md +37 -0
- package/templates/universal/rules/git-workflow.md +39 -0
- package/templates/universal/rules/security.md +39 -0
- package/templates/universal/rules/testing.md +38 -0
- package/templates/universal/template.json +6 -0
- package/ui/dist/assets/index-C5apzulu.css +32 -0
- package/ui/dist/assets/index-CBNCwCnY.js +489 -0
- package/ui/dist/index.html +14 -0
- package/ui/server.cjs +2237 -0
- package/ui/terminal-server.cjs +160 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 regression.io
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Claude Config
|
|
2
|
+
|
|
3
|
+
Configuration management for **Claude Code** with CLI and optional Web UI.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @regression-io/claude-config
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or from GitHub:
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g github:regression-io/claude-config
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Initialize a project with a template
|
|
20
|
+
claude-config init --template fastapi
|
|
21
|
+
|
|
22
|
+
# Add MCPs to your project
|
|
23
|
+
claude-config add postgres github
|
|
24
|
+
|
|
25
|
+
# Generate .mcp.json for Claude Code
|
|
26
|
+
claude-config apply
|
|
27
|
+
|
|
28
|
+
# Or open the Web UI
|
|
29
|
+
claude-config ui
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## CLI Commands
|
|
33
|
+
|
|
34
|
+
### Project Commands
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
claude-config init [--template <name>] # Initialize project
|
|
38
|
+
claude-config apply # Generate .mcp.json from config
|
|
39
|
+
claude-config apply-template <name> # Add template to existing project
|
|
40
|
+
claude-config show # Show current project config
|
|
41
|
+
claude-config list # List available MCPs (✓ = active)
|
|
42
|
+
claude-config templates # List available templates
|
|
43
|
+
claude-config add <mcp> [mcp...] # Add MCP(s) to project
|
|
44
|
+
claude-config remove <mcp> [mcp...] # Remove MCP(s) from project
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Memory Commands
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
claude-config memory # Show memory status
|
|
51
|
+
claude-config memory init # Initialize project memory
|
|
52
|
+
claude-config memory add <type> "<content>" # Add entry
|
|
53
|
+
claude-config memory search <query> # Search all memory
|
|
54
|
+
|
|
55
|
+
# Types: preference, correction, fact (global)
|
|
56
|
+
# context, pattern, decision, issue, history (project)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Environment Commands
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
claude-config env # List environment variables
|
|
63
|
+
claude-config env set <KEY> <value> # Set variable in .claude/.env
|
|
64
|
+
claude-config env unset <KEY> # Remove variable
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Project Commands
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
claude-config project # List registered projects
|
|
71
|
+
claude-config project add [path] # Add project (defaults to cwd)
|
|
72
|
+
claude-config project add [path] --name X # Add with custom display name
|
|
73
|
+
claude-config project remove <name|path> # Remove from registry
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Registry Commands
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
claude-config registry-add <name> '<json>' # Add MCP to global registry
|
|
80
|
+
claude-config registry-remove <name> # Remove MCP from registry
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Web UI
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
claude-config ui # Start UI on port 3333
|
|
87
|
+
claude-config ui --port 8080 # Custom port
|
|
88
|
+
claude-config ui /path/to/project # Specific project directory
|
|
89
|
+
claude-config ui --daemon # Run as background daemon
|
|
90
|
+
claude-config ui status # Check if daemon is running
|
|
91
|
+
claude-config ui stop # Stop the daemon
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Daemon Mode**: Run `claude-config ui --daemon` to start the UI as a background service.
|
|
95
|
+
The UI runs from your home directory and persists across terminal sessions.
|
|
96
|
+
Switch between registered projects using the dropdown in the header.
|
|
97
|
+
|
|
98
|
+
## Templates
|
|
99
|
+
|
|
100
|
+
Initialize projects with pre-configured rules and settings:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# List available templates
|
|
104
|
+
claude-config templates
|
|
105
|
+
|
|
106
|
+
# Frameworks
|
|
107
|
+
claude-config init --template fastapi
|
|
108
|
+
claude-config init --template react-ts
|
|
109
|
+
claude-config init --template python-cli
|
|
110
|
+
claude-config init --template mcp-python
|
|
111
|
+
|
|
112
|
+
# Languages
|
|
113
|
+
claude-config init --template python
|
|
114
|
+
claude-config init --template typescript
|
|
115
|
+
|
|
116
|
+
# Monorepos
|
|
117
|
+
claude-config init --template fastapi-react-ts
|
|
118
|
+
claude-config init --template fastapi-react-js
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Add templates to existing projects:
|
|
122
|
+
```bash
|
|
123
|
+
claude-config apply-template python
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Shell Integration
|
|
127
|
+
|
|
128
|
+
For auto-apply on directory change, add to `~/.zshrc`:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
source /path/to/claude-config/shell/claude-config.zsh
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
This enables:
|
|
135
|
+
- Auto-generates `.mcp.json` when entering a project with `.claude/mcps.json`
|
|
136
|
+
- Tab completion for all commands
|
|
137
|
+
|
|
138
|
+
## Configuration Hierarchy
|
|
139
|
+
|
|
140
|
+
Settings merge from global to project:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
~/.claude/mcps.json # Global - applies everywhere
|
|
144
|
+
~/projects/.claude/mcps.json # Workspace - applies to projects here
|
|
145
|
+
~/projects/my-app/.claude/ # Project - specific to this project
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Project Structure
|
|
149
|
+
|
|
150
|
+
After `claude-config init`:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
your-project/
|
|
154
|
+
├── .claude/
|
|
155
|
+
│ ├── mcps.json # MCP configuration
|
|
156
|
+
│ ├── settings.json # Claude Code settings
|
|
157
|
+
│ ├── rules/ # Project rules (*.md)
|
|
158
|
+
│ └── commands/ # Custom commands (*.md)
|
|
159
|
+
└── .mcp.json # Generated - Claude Code reads this
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## MCP Configuration
|
|
163
|
+
|
|
164
|
+
`.claude/mcps.json`:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"mcpServers": {
|
|
169
|
+
"filesystem": {
|
|
170
|
+
"command": "npx",
|
|
171
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"]
|
|
172
|
+
},
|
|
173
|
+
"github": {
|
|
174
|
+
"command": "npx",
|
|
175
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
176
|
+
"env": {
|
|
177
|
+
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Environment variables use `${VAR}` syntax and load from `.claude/.env`.
|
|
185
|
+
|
|
186
|
+
## Memory System
|
|
187
|
+
|
|
188
|
+
Persistent memory for Claude Code sessions:
|
|
189
|
+
|
|
190
|
+
**Global Memory** (`~/.claude/memory/`):
|
|
191
|
+
- `preferences.md` - User preferences (tools, style)
|
|
192
|
+
- `corrections.md` - Mistakes to avoid
|
|
193
|
+
- `facts.md` - Environment facts
|
|
194
|
+
|
|
195
|
+
**Project Memory** (`<project>/.claude/memory/`):
|
|
196
|
+
- `context.md` - Project overview
|
|
197
|
+
- `patterns.md` - Code patterns
|
|
198
|
+
- `decisions.md` - Architecture decisions
|
|
199
|
+
- `issues.md` - Known issues
|
|
200
|
+
- `history.md` - Session history
|
|
201
|
+
|
|
202
|
+
Manage via Web UI or edit files directly.
|
|
203
|
+
|
|
204
|
+
## Web UI Features
|
|
205
|
+
|
|
206
|
+
When you run `claude-config ui`:
|
|
207
|
+
|
|
208
|
+
- **Project Switcher** - Switch between registered projects from header dropdown
|
|
209
|
+
- **File Explorer** - Browse/edit all .claude folders in hierarchy
|
|
210
|
+
- **MCP Registry** - Search GitHub/npm, add/edit/delete MCPs
|
|
211
|
+
- **Claude Code Settings** - Visual editor for `~/.claude/settings.json`
|
|
212
|
+
- Permissions (allow/ask/deny rules)
|
|
213
|
+
- Model selection
|
|
214
|
+
- Behavior settings
|
|
215
|
+
- Hooks and advanced options
|
|
216
|
+
- **Memory System** - Manage preferences, corrections, patterns, decisions
|
|
217
|
+
- **Templates** - Apply rule templates to projects
|
|
218
|
+
- **Preferences** - Configure claude-config tool settings
|
|
219
|
+
- **One-Click Updates** - Update badge appears when new version available
|
|
220
|
+
|
|
221
|
+
## Claude Code Settings
|
|
222
|
+
|
|
223
|
+
The Web UI provides a visual editor for `~/.claude/settings.json`:
|
|
224
|
+
|
|
225
|
+
### Permissions
|
|
226
|
+
Configure what Claude Code can do automatically:
|
|
227
|
+
- **Allow** - Tools that run without asking
|
|
228
|
+
- **Ask** - Tools that require confirmation
|
|
229
|
+
- **Deny** - Tools that are blocked
|
|
230
|
+
|
|
231
|
+
Pattern examples:
|
|
232
|
+
```
|
|
233
|
+
Bash(npm run build) # Specific command
|
|
234
|
+
Bash(npm:*) # Prefix match (npm anything)
|
|
235
|
+
Read(**) # All file reads
|
|
236
|
+
Edit(src/**) # Edit files in src/
|
|
237
|
+
mcp__github__* # All GitHub MCP tools
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Model Selection
|
|
241
|
+
Choose your preferred Claude model (Sonnet 4, Opus 4.5, etc.)
|
|
242
|
+
|
|
243
|
+
### Behavior
|
|
244
|
+
- Auto-accept edits
|
|
245
|
+
- Verbose mode
|
|
246
|
+
- Enable/disable MCP servers
|
|
247
|
+
|
|
248
|
+
## Preferences
|
|
249
|
+
|
|
250
|
+
User settings stored in `~/.claude-config/config.json`:
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"toolsDir": "~/mcp-tools",
|
|
255
|
+
"registryPath": "~/.claude/registry.json",
|
|
256
|
+
"ui": {
|
|
257
|
+
"port": 3333,
|
|
258
|
+
"openBrowser": true
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
| Key | Description |
|
|
264
|
+
|-----|-------------|
|
|
265
|
+
| `toolsDir` | Directory for local MCP tools |
|
|
266
|
+
| `registryPath` | Path to custom MCP registry |
|
|
267
|
+
| `ui.port` | Default port for web UI |
|
|
268
|
+
| `ui.openBrowser` | Auto-open browser on `claude-config ui` |
|
|
269
|
+
|
|
270
|
+
## Requirements
|
|
271
|
+
|
|
272
|
+
- Node.js 18+
|
|
273
|
+
|
|
274
|
+
## Development
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
git clone https://github.com/regression-io/claude-config.git
|
|
278
|
+
cd claude-config
|
|
279
|
+
npm install
|
|
280
|
+
npm run build
|
|
281
|
+
npm start
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
MIT
|
package/cli.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Claude Config CLI
|
|
5
|
+
*
|
|
6
|
+
* Configuration management for Claude Code
|
|
7
|
+
* CLI-first with optional Web UI
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const { spawn, execSync } = require('child_process');
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const command = args[0] || '';
|
|
17
|
+
|
|
18
|
+
// PID file for daemon mode
|
|
19
|
+
const PID_FILE = path.join(os.homedir(), '.claude-config', 'ui.pid');
|
|
20
|
+
|
|
21
|
+
// UI command needs special handling (starts web server with better error handling)
|
|
22
|
+
if (command === 'ui' || command === 'web' || command === 'server') {
|
|
23
|
+
// Check for subcommand: ui stop, ui status
|
|
24
|
+
const subcommand = args[1];
|
|
25
|
+
if (subcommand === 'stop') {
|
|
26
|
+
stopDaemon();
|
|
27
|
+
} else if (subcommand === 'status') {
|
|
28
|
+
checkDaemonStatus();
|
|
29
|
+
} else {
|
|
30
|
+
startUI();
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
// Pass everything to config-loader.js
|
|
34
|
+
const loaderPath = path.join(__dirname, 'config-loader.js');
|
|
35
|
+
const child = spawn('node', [loaderPath, ...args], {
|
|
36
|
+
stdio: 'inherit',
|
|
37
|
+
cwd: process.cwd()
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
child.on('close', (code) => {
|
|
41
|
+
process.exit(code || 0);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function stopDaemon() {
|
|
46
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
47
|
+
console.log('No daemon running (PID file not found)');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim());
|
|
53
|
+
process.kill(pid, 'SIGTERM');
|
|
54
|
+
fs.unlinkSync(PID_FILE);
|
|
55
|
+
console.log(`Stopped daemon (PID: ${pid})`);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (err.code === 'ESRCH') {
|
|
58
|
+
// Process doesn't exist, clean up PID file
|
|
59
|
+
fs.unlinkSync(PID_FILE);
|
|
60
|
+
console.log('Daemon was not running, cleaned up stale PID file');
|
|
61
|
+
} else {
|
|
62
|
+
console.error('Failed to stop daemon:', err.message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function checkDaemonStatus() {
|
|
68
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
69
|
+
console.log('Daemon: not running');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim());
|
|
75
|
+
// Check if process is running
|
|
76
|
+
process.kill(pid, 0);
|
|
77
|
+
console.log(`Daemon: running (PID: ${pid})`);
|
|
78
|
+
console.log(`UI available at: http://localhost:3333`);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.log('Daemon: not running (stale PID file)');
|
|
81
|
+
fs.unlinkSync(PID_FILE);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function startDaemon(flags) {
|
|
86
|
+
// Check if already running
|
|
87
|
+
if (fs.existsSync(PID_FILE)) {
|
|
88
|
+
try {
|
|
89
|
+
const existingPid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim());
|
|
90
|
+
process.kill(existingPid, 0);
|
|
91
|
+
console.log(`Daemon already running (PID: ${existingPid})`);
|
|
92
|
+
console.log(`UI available at: http://localhost:${flags.port}`);
|
|
93
|
+
console.log('Use "claude-config ui stop" to stop the daemon');
|
|
94
|
+
return;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
// Process not running, clean up stale PID file
|
|
97
|
+
fs.unlinkSync(PID_FILE);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Ensure PID directory exists
|
|
102
|
+
const pidDir = path.dirname(PID_FILE);
|
|
103
|
+
if (!fs.existsSync(pidDir)) {
|
|
104
|
+
fs.mkdirSync(pidDir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Log file for daemon output
|
|
108
|
+
const logFile = path.join(pidDir, 'ui.log');
|
|
109
|
+
|
|
110
|
+
// Build args for spawned process (without --daemon flag)
|
|
111
|
+
const spawnArgs = ['ui'];
|
|
112
|
+
if (flags.port !== 3333) {
|
|
113
|
+
spawnArgs.push('--port', String(flags.port));
|
|
114
|
+
}
|
|
115
|
+
if (flags.dir) {
|
|
116
|
+
spawnArgs.push('--dir', flags.dir);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Spawn detached process
|
|
120
|
+
const out = fs.openSync(logFile, 'a');
|
|
121
|
+
const err = fs.openSync(logFile, 'a');
|
|
122
|
+
|
|
123
|
+
const child = spawn(process.execPath, [__filename, ...spawnArgs], {
|
|
124
|
+
detached: true,
|
|
125
|
+
stdio: ['ignore', out, err],
|
|
126
|
+
cwd: os.homedir()
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Write PID file
|
|
130
|
+
fs.writeFileSync(PID_FILE, String(child.pid));
|
|
131
|
+
|
|
132
|
+
// Unref to allow parent to exit
|
|
133
|
+
child.unref();
|
|
134
|
+
|
|
135
|
+
console.log(`Started daemon (PID: ${child.pid})`);
|
|
136
|
+
console.log(`UI available at: http://localhost:${flags.port}`);
|
|
137
|
+
console.log(`Logs: ${logFile}`);
|
|
138
|
+
console.log('\nCommands:');
|
|
139
|
+
console.log(' claude-config ui status - Check daemon status');
|
|
140
|
+
console.log(' claude-config ui stop - Stop the daemon');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function startUI() {
|
|
144
|
+
// Parse UI-specific flags
|
|
145
|
+
const flags = {
|
|
146
|
+
port: 3333,
|
|
147
|
+
dir: null, // Will default to active project or home
|
|
148
|
+
daemon: false
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
for (let i = 1; i < args.length; i++) {
|
|
152
|
+
const arg = args[i];
|
|
153
|
+
if (arg === '--port' || arg === '-p') {
|
|
154
|
+
const portArg = args[++i];
|
|
155
|
+
if (!portArg || isNaN(parseInt(portArg))) {
|
|
156
|
+
console.error('Error: --port requires a valid port number');
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
flags.port = parseInt(portArg);
|
|
160
|
+
} else if (arg.startsWith('--port=')) {
|
|
161
|
+
const portVal = parseInt(arg.split('=')[1]);
|
|
162
|
+
if (isNaN(portVal)) {
|
|
163
|
+
console.error('Error: --port requires a valid port number');
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
flags.port = portVal;
|
|
167
|
+
} else if (arg === '--dir' || arg === '-d') {
|
|
168
|
+
flags.dir = args[++i] || null;
|
|
169
|
+
} else if (arg.startsWith('--dir=')) {
|
|
170
|
+
flags.dir = arg.split('=')[1] || null;
|
|
171
|
+
} else if (arg === '--daemon' || arg === '-D') {
|
|
172
|
+
flags.daemon = true;
|
|
173
|
+
} else if (!arg.startsWith('-') && fs.existsSync(arg) && fs.statSync(arg).isDirectory()) {
|
|
174
|
+
flags.dir = arg;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Daemon mode: spawn detached and exit
|
|
179
|
+
if (flags.daemon) {
|
|
180
|
+
return startDaemon(flags);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Validate port range
|
|
184
|
+
if (flags.port < 1 || flags.port > 65535) {
|
|
185
|
+
console.error('Error: Port must be between 1 and 65535');
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Validate directory exists (if specified)
|
|
190
|
+
if (flags.dir) {
|
|
191
|
+
if (!fs.existsSync(flags.dir)) {
|
|
192
|
+
console.error(`Error: Directory not found: ${flags.dir}`);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!fs.statSync(flags.dir).isDirectory()) {
|
|
197
|
+
console.error(`Error: Not a directory: ${flags.dir}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
flags.dir = path.resolve(flags.dir);
|
|
202
|
+
}
|
|
203
|
+
// If no dir specified, server will load from projects registry or use cwd
|
|
204
|
+
|
|
205
|
+
// Load dependencies
|
|
206
|
+
const serverPath = path.join(__dirname, 'ui', 'server.cjs');
|
|
207
|
+
|
|
208
|
+
if (!fs.existsSync(serverPath)) {
|
|
209
|
+
console.error('Error: UI server not found.');
|
|
210
|
+
console.error('The package may not be installed correctly.');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const distPath = path.join(__dirname, 'ui', 'dist');
|
|
215
|
+
if (!fs.existsSync(distPath)) {
|
|
216
|
+
console.error('Error: UI build not found.');
|
|
217
|
+
console.error('Run "npm run build" to build the UI first.');
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let ConfigUIServer, ClaudeConfigManager;
|
|
222
|
+
try {
|
|
223
|
+
ConfigUIServer = require(serverPath);
|
|
224
|
+
ClaudeConfigManager = require('./config-loader.js');
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error('Error: Failed to load dependencies');
|
|
227
|
+
console.error(err.message);
|
|
228
|
+
if (err.message.includes('node-pty')) {
|
|
229
|
+
console.error('\nThe terminal feature requires node-pty which failed to load.');
|
|
230
|
+
console.error('Try reinstalling: npm rebuild node-pty');
|
|
231
|
+
}
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Handle server errors
|
|
236
|
+
process.on('uncaughtException', (err) => {
|
|
237
|
+
if (err.code === 'EADDRINUSE') {
|
|
238
|
+
console.error(`\nError: Port ${flags.port} is already in use.`);
|
|
239
|
+
console.error(`Try a different port: claude-config ui --port ${flags.port + 1}`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
} else if (err.code === 'EACCES') {
|
|
242
|
+
console.error(`\nError: Permission denied for port ${flags.port}.`);
|
|
243
|
+
console.error('Ports below 1024 require elevated privileges.');
|
|
244
|
+
process.exit(1);
|
|
245
|
+
} else {
|
|
246
|
+
console.error('\nUnexpected error:', err.message);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const manager = new ClaudeConfigManager();
|
|
253
|
+
const server = new ConfigUIServer(flags.port, flags.dir, manager);
|
|
254
|
+
server.start();
|
|
255
|
+
} catch (err) {
|
|
256
|
+
console.error('Error: Failed to start server');
|
|
257
|
+
console.error(err.message);
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
}
|