coder-config 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +553 -0
- package/cli.js +431 -0
- package/config-loader.js +294 -0
- package/hooks/activity-track.sh +56 -0
- package/hooks/codex-workstream.sh +44 -0
- package/hooks/gemini-workstream.sh +44 -0
- package/hooks/workstream-inject.sh +20 -0
- package/lib/activity.js +283 -0
- package/lib/apply.js +344 -0
- package/lib/cli.js +267 -0
- package/lib/config.js +171 -0
- package/lib/constants.js +55 -0
- package/lib/env.js +114 -0
- package/lib/index.js +47 -0
- package/lib/init.js +122 -0
- package/lib/mcps.js +139 -0
- package/lib/memory.js +201 -0
- package/lib/projects.js +138 -0
- package/lib/registry.js +83 -0
- package/lib/utils.js +129 -0
- package/lib/workstreams.js +652 -0
- package/package.json +80 -0
- package/scripts/capture-screenshots.js +142 -0
- package/scripts/postinstall.js +122 -0
- package/scripts/release.sh +71 -0
- package/scripts/sync-version.js +77 -0
- package/scripts/tauri-prepare.js +328 -0
- package/shared/mcp-registry.json +76 -0
- package/ui/dist/assets/index-DbZ3_HBD.js +3204 -0
- package/ui/dist/assets/index-DjLdm3Mr.css +32 -0
- package/ui/dist/icons/icon-192.svg +16 -0
- package/ui/dist/icons/icon-512.svg +16 -0
- package/ui/dist/index.html +39 -0
- package/ui/dist/manifest.json +25 -0
- package/ui/dist/sw.js +24 -0
- package/ui/dist/tutorial/claude-settings.png +0 -0
- package/ui/dist/tutorial/header.png +0 -0
- package/ui/dist/tutorial/mcp-registry.png +0 -0
- package/ui/dist/tutorial/memory-view.png +0 -0
- package/ui/dist/tutorial/permissions.png +0 -0
- package/ui/dist/tutorial/plugins-view.png +0 -0
- package/ui/dist/tutorial/project-explorer.png +0 -0
- package/ui/dist/tutorial/projects-view.png +0 -0
- package/ui/dist/tutorial/sidebar.png +0 -0
- package/ui/dist/tutorial/tutorial-view.png +0 -0
- package/ui/dist/tutorial/workstreams-view.png +0 -0
- package/ui/routes/activity.js +58 -0
- package/ui/routes/commands.js +74 -0
- package/ui/routes/configs.js +329 -0
- package/ui/routes/env.js +40 -0
- package/ui/routes/file-explorer.js +668 -0
- package/ui/routes/index.js +41 -0
- package/ui/routes/mcp-discovery.js +235 -0
- package/ui/routes/memory.js +385 -0
- package/ui/routes/package.json +3 -0
- package/ui/routes/plugins.js +466 -0
- package/ui/routes/projects.js +198 -0
- package/ui/routes/registry.js +30 -0
- package/ui/routes/rules.js +74 -0
- package/ui/routes/search.js +125 -0
- package/ui/routes/settings.js +381 -0
- package/ui/routes/subprojects.js +208 -0
- package/ui/routes/tool-sync.js +127 -0
- package/ui/routes/updates.js +339 -0
- package/ui/routes/workstreams.js +224 -0
- package/ui/server.cjs +773 -0
- package/ui/terminal-server.cjs +160 -0
package/lib/cli.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command handler
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { VERSION, TOOL_PATHS } = require('./constants');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run CLI command
|
|
10
|
+
*/
|
|
11
|
+
function runCli(manager) {
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const command = args[0];
|
|
14
|
+
|
|
15
|
+
switch (command) {
|
|
16
|
+
// Core
|
|
17
|
+
case 'init':
|
|
18
|
+
manager.init(args[1]);
|
|
19
|
+
break;
|
|
20
|
+
case 'apply':
|
|
21
|
+
manager.apply(args[1]);
|
|
22
|
+
break;
|
|
23
|
+
case 'show':
|
|
24
|
+
manager.show(args[1]);
|
|
25
|
+
break;
|
|
26
|
+
case 'list':
|
|
27
|
+
case 'mcps':
|
|
28
|
+
manager.list();
|
|
29
|
+
break;
|
|
30
|
+
|
|
31
|
+
// Edit MCPs
|
|
32
|
+
case 'add':
|
|
33
|
+
manager.add(args.slice(1));
|
|
34
|
+
break;
|
|
35
|
+
case 'remove':
|
|
36
|
+
case 'rm':
|
|
37
|
+
manager.remove(args.slice(1));
|
|
38
|
+
break;
|
|
39
|
+
|
|
40
|
+
// Registry management
|
|
41
|
+
case 'registry':
|
|
42
|
+
if (args[1] === 'add') {
|
|
43
|
+
manager.registryAdd(args[2], args[3]);
|
|
44
|
+
} else if (args[1] === 'remove' || args[1] === 'rm') {
|
|
45
|
+
manager.registryRemove(args[2]);
|
|
46
|
+
} else {
|
|
47
|
+
manager.registryList();
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
// Memory
|
|
52
|
+
case 'memory':
|
|
53
|
+
if (args[1] === 'init') {
|
|
54
|
+
manager.memoryInit(args[2]);
|
|
55
|
+
} else if (args[1] === 'add') {
|
|
56
|
+
manager.memoryAdd(args[2], args.slice(3).join(' '));
|
|
57
|
+
} else if (args[1] === 'search') {
|
|
58
|
+
manager.memorySearch(args.slice(2).join(' '));
|
|
59
|
+
} else {
|
|
60
|
+
manager.memoryList();
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
// Environment
|
|
65
|
+
case 'env':
|
|
66
|
+
if (args[1] === 'set') {
|
|
67
|
+
manager.envSet(args[2], args[3]);
|
|
68
|
+
} else if (args[1] === 'unset') {
|
|
69
|
+
manager.envUnset(args[2]);
|
|
70
|
+
} else {
|
|
71
|
+
manager.envList();
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
|
|
75
|
+
// Project registry (for UI)
|
|
76
|
+
case 'project':
|
|
77
|
+
case 'projects':
|
|
78
|
+
if (args[1] === 'add') {
|
|
79
|
+
const nameIdx = args.indexOf('--name');
|
|
80
|
+
const name = nameIdx !== -1 ? args[nameIdx + 1] : null;
|
|
81
|
+
const projectPath = args[2] && !args[2].startsWith('--') ? args[2] : process.cwd();
|
|
82
|
+
manager.projectAdd(projectPath, name);
|
|
83
|
+
} else if (args[1] === 'remove' || args[1] === 'rm') {
|
|
84
|
+
manager.projectRemove(args[2]);
|
|
85
|
+
} else {
|
|
86
|
+
manager.projectList();
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
// Workstreams
|
|
91
|
+
case 'workstream':
|
|
92
|
+
case 'ws':
|
|
93
|
+
if (args[1] === 'create' || args[1] === 'new') {
|
|
94
|
+
manager.workstreamCreate(args[2]);
|
|
95
|
+
} else if (args[1] === 'delete' || args[1] === 'rm') {
|
|
96
|
+
manager.workstreamDelete(args[2]);
|
|
97
|
+
} else if (args[1] === 'use' || args[1] === 'switch') {
|
|
98
|
+
manager.workstreamUse(args[2]);
|
|
99
|
+
} else if (args[1] === 'add') {
|
|
100
|
+
manager.workstreamAddProject(args[2], args[3]);
|
|
101
|
+
} else if (args[1] === 'remove' || args[1] === 'rm') {
|
|
102
|
+
manager.workstreamRemoveProject(args[2], args[3]);
|
|
103
|
+
} else if (args[1] === 'inject') {
|
|
104
|
+
const silent = args.includes('--silent') || args.includes('-s');
|
|
105
|
+
manager.workstreamInject(silent);
|
|
106
|
+
} else if (args[1] === 'detect') {
|
|
107
|
+
const ws = manager.workstreamDetect(args[2] || process.cwd());
|
|
108
|
+
if (ws) {
|
|
109
|
+
console.log(ws.name);
|
|
110
|
+
}
|
|
111
|
+
} else if (args[1] === 'active') {
|
|
112
|
+
const ws = manager.workstreamActive();
|
|
113
|
+
if (ws) {
|
|
114
|
+
console.log(`Active: ${ws.name}`);
|
|
115
|
+
if (ws.projects && ws.projects.length > 0) {
|
|
116
|
+
console.log(`Projects: ${ws.projects.map(p => path.basename(p)).join(', ')}`);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
console.log('No active workstream');
|
|
120
|
+
}
|
|
121
|
+
} else if (args[1] === 'install-hook') {
|
|
122
|
+
const forGemini = args.includes('--gemini') || args.includes('-g');
|
|
123
|
+
const forCodex = args.includes('--codex') || args.includes('-x');
|
|
124
|
+
const forAll = args.includes('--all') || args.includes('-a');
|
|
125
|
+
if (forAll) {
|
|
126
|
+
manager.workstreamInstallHook();
|
|
127
|
+
manager.workstreamInstallHookGemini();
|
|
128
|
+
manager.workstreamInstallHookCodex();
|
|
129
|
+
} else if (forGemini) {
|
|
130
|
+
manager.workstreamInstallHookGemini();
|
|
131
|
+
} else if (forCodex) {
|
|
132
|
+
manager.workstreamInstallHookCodex();
|
|
133
|
+
} else {
|
|
134
|
+
manager.workstreamInstallHook();
|
|
135
|
+
}
|
|
136
|
+
} else if (args[1] === 'deactivate') {
|
|
137
|
+
manager.workstreamDeactivate();
|
|
138
|
+
} else if (args[1] === 'check-path') {
|
|
139
|
+
const targetPath = args[2];
|
|
140
|
+
if (!targetPath) {
|
|
141
|
+
console.error('Usage: claude-config workstream check-path <path>');
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
const silent = args.includes('--silent') || args.includes('-s');
|
|
145
|
+
const isValid = manager.workstreamCheckPath(targetPath, silent);
|
|
146
|
+
process.exit(isValid ? 0 : 1);
|
|
147
|
+
} else {
|
|
148
|
+
manager.workstreamList();
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
// Maintenance
|
|
153
|
+
case 'update':
|
|
154
|
+
manager.update(args.slice(1)).catch(err => {
|
|
155
|
+
console.error('Update error:', err.message);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
});
|
|
158
|
+
break;
|
|
159
|
+
case 'ui': {
|
|
160
|
+
const UIServer = require('../ui/server.cjs');
|
|
161
|
+
const port = parseInt(args.find(a => a.startsWith('--port='))?.split('=')[1] || '3333');
|
|
162
|
+
const uiDir = args.find(a => !a.startsWith('--') && a !== 'ui') || process.cwd();
|
|
163
|
+
const uiServer = new UIServer(port, uiDir, manager);
|
|
164
|
+
uiServer.start();
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case 'version':
|
|
168
|
+
case '-v':
|
|
169
|
+
case '--version':
|
|
170
|
+
manager.version();
|
|
171
|
+
break;
|
|
172
|
+
|
|
173
|
+
default:
|
|
174
|
+
printHelp();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Print help message
|
|
180
|
+
*/
|
|
181
|
+
function printHelp() {
|
|
182
|
+
console.log(`
|
|
183
|
+
coder-config v${VERSION}
|
|
184
|
+
Configuration manager for AI coding tools
|
|
185
|
+
|
|
186
|
+
Usage:
|
|
187
|
+
coder-config <command> [args]
|
|
188
|
+
claude-config <command> [args] (alias)
|
|
189
|
+
|
|
190
|
+
Project Commands:
|
|
191
|
+
init Initialize project with .claude/mcps.json
|
|
192
|
+
apply Generate .mcp.json from config
|
|
193
|
+
show Show current project config
|
|
194
|
+
list List available MCPs (ā = active)
|
|
195
|
+
add <mcp> [mcp...] Add MCP(s) to project
|
|
196
|
+
remove <mcp> [mcp...] Remove MCP(s) from project
|
|
197
|
+
|
|
198
|
+
Memory Commands:
|
|
199
|
+
memory Show memory status
|
|
200
|
+
memory init Initialize project memory
|
|
201
|
+
memory add <type> <content> Add entry (types: preference, correction, fact,
|
|
202
|
+
context, pattern, decision, issue, history)
|
|
203
|
+
memory search <query> Search all memory files
|
|
204
|
+
|
|
205
|
+
Environment Commands:
|
|
206
|
+
env List environment variables
|
|
207
|
+
env set <KEY> <value> Set variable in .claude/.env
|
|
208
|
+
env unset <KEY> Remove variable
|
|
209
|
+
|
|
210
|
+
Project Commands (for UI):
|
|
211
|
+
project List registered projects
|
|
212
|
+
project add [path] Add project (defaults to cwd)
|
|
213
|
+
project add [path] --name X Add with custom display name
|
|
214
|
+
project remove <name|path> Remove project from registry
|
|
215
|
+
|
|
216
|
+
Workstream Commands:
|
|
217
|
+
workstream List all workstreams
|
|
218
|
+
workstream create "Name" Create new workstream
|
|
219
|
+
workstream delete <name> Delete workstream
|
|
220
|
+
workstream use <name> Set active workstream (global)
|
|
221
|
+
workstream active Show current active workstream
|
|
222
|
+
workstream deactivate Show how to deactivate workstream
|
|
223
|
+
workstream add <ws> <path> Add project to workstream
|
|
224
|
+
workstream remove <ws> <path> Remove project from workstream
|
|
225
|
+
workstream inject [--silent] Output restriction + context (for hooks)
|
|
226
|
+
workstream detect [path] Detect workstream for directory
|
|
227
|
+
workstream install-hook Install pre-prompt hook for Claude Code
|
|
228
|
+
workstream install-hook --gemini Install for Gemini CLI
|
|
229
|
+
workstream install-hook --codex Install for Codex CLI
|
|
230
|
+
workstream install-hook --all Install for all supported tools
|
|
231
|
+
|
|
232
|
+
Per-session activation (enables parallel work):
|
|
233
|
+
export CLAUDE_WORKSTREAM=<name-or-id>
|
|
234
|
+
|
|
235
|
+
Registry Commands:
|
|
236
|
+
registry List MCPs in global registry
|
|
237
|
+
registry add <name> '<json>' Add MCP to global registry
|
|
238
|
+
registry remove <name> Remove MCP from registry
|
|
239
|
+
|
|
240
|
+
Maintenance:
|
|
241
|
+
update Check npm for updates and install if available
|
|
242
|
+
update --check Check for updates without installing
|
|
243
|
+
update /path/to/source Update from local development source
|
|
244
|
+
ui [--port=3333] Start web UI (daemon mode by default)
|
|
245
|
+
ui install Auto-start on login (macOS LaunchAgent)
|
|
246
|
+
ui uninstall Remove auto-start
|
|
247
|
+
ui status Check if UI daemon is running
|
|
248
|
+
ui stop Stop the UI daemon
|
|
249
|
+
version Show version info
|
|
250
|
+
|
|
251
|
+
Plugins (use Claude Code CLI):
|
|
252
|
+
claude plugin marketplace add regression-io/claude-config-plugins
|
|
253
|
+
claude plugin install fastapi-support@claude-config-plugins
|
|
254
|
+
|
|
255
|
+
Examples:
|
|
256
|
+
coder-config init
|
|
257
|
+
coder-config add postgres github
|
|
258
|
+
coder-config memory add preference "Use TypeScript for new files"
|
|
259
|
+
coder-config env set GITHUB_TOKEN ghp_xxx
|
|
260
|
+
coder-config apply
|
|
261
|
+
`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.exports = {
|
|
265
|
+
runCli,
|
|
266
|
+
printHelp,
|
|
267
|
+
};
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config finding and merging utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { TOOL_PATHS } = require('./constants');
|
|
8
|
+
const { loadJson } = require('./utils');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Find project root (has .claude/ directory)
|
|
12
|
+
*/
|
|
13
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
14
|
+
let dir = path.resolve(startDir);
|
|
15
|
+
const root = path.parse(dir).root;
|
|
16
|
+
while (dir !== root) {
|
|
17
|
+
if (fs.existsSync(path.join(dir, '.claude'))) {
|
|
18
|
+
return dir;
|
|
19
|
+
}
|
|
20
|
+
dir = path.dirname(dir);
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Find ALL .claude/mcps.json configs from cwd up to root (and ~/.claude)
|
|
27
|
+
* Returns array from root to leaf (so child overrides parent when merged)
|
|
28
|
+
*/
|
|
29
|
+
function findAllConfigs(startDir = process.cwd()) {
|
|
30
|
+
const configs = [];
|
|
31
|
+
let dir = path.resolve(startDir);
|
|
32
|
+
const root = path.parse(dir).root;
|
|
33
|
+
const homeDir = process.env.HOME || '';
|
|
34
|
+
|
|
35
|
+
while (dir !== root) {
|
|
36
|
+
const configPath = path.join(dir, '.claude', 'mcps.json');
|
|
37
|
+
if (fs.existsSync(configPath)) {
|
|
38
|
+
configs.unshift({ dir, configPath });
|
|
39
|
+
}
|
|
40
|
+
dir = path.dirname(dir);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const homeConfig = path.join(homeDir, '.claude', 'mcps.json');
|
|
44
|
+
if (fs.existsSync(homeConfig)) {
|
|
45
|
+
if (!configs.some(c => c.configPath === homeConfig)) {
|
|
46
|
+
configs.unshift({ dir: homeDir, configPath: homeConfig });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return configs;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Merge multiple configs (later ones override earlier)
|
|
55
|
+
*/
|
|
56
|
+
function mergeConfigs(configs) {
|
|
57
|
+
const merged = {
|
|
58
|
+
include: [],
|
|
59
|
+
mcpServers: {},
|
|
60
|
+
enabledPlugins: {},
|
|
61
|
+
template: null
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
for (const { config } of configs) {
|
|
65
|
+
if (!config) continue;
|
|
66
|
+
|
|
67
|
+
if (config.include && Array.isArray(config.include)) {
|
|
68
|
+
for (const mcp of config.include) {
|
|
69
|
+
if (!merged.include.includes(mcp)) {
|
|
70
|
+
merged.include.push(mcp);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (config.mcpServers) {
|
|
76
|
+
Object.assign(merged.mcpServers, config.mcpServers);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Merge enabledPlugins - child overrides parent
|
|
80
|
+
// false explicitly disables a parent-enabled plugin
|
|
81
|
+
if (config.enabledPlugins) {
|
|
82
|
+
Object.assign(merged.enabledPlugins, config.enabledPlugins);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (config.template) {
|
|
86
|
+
merged.template = config.template;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return merged;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get project config path
|
|
95
|
+
*/
|
|
96
|
+
function getConfigPath(installDir, projectDir = null) {
|
|
97
|
+
const dir = projectDir || findProjectRoot() || process.cwd();
|
|
98
|
+
return path.join(dir, '.claude', 'mcps.json');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Collect files (rules or commands) from all directories in hierarchy
|
|
103
|
+
*/
|
|
104
|
+
function collectFilesFromHierarchy(configLocations, subdir) {
|
|
105
|
+
const fileMap = new Map();
|
|
106
|
+
|
|
107
|
+
for (const { dir } of configLocations) {
|
|
108
|
+
const dirPath = path.join(dir, '.claude', subdir);
|
|
109
|
+
if (fs.existsSync(dirPath)) {
|
|
110
|
+
const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.md'));
|
|
111
|
+
for (const file of files) {
|
|
112
|
+
fileMap.set(file, {
|
|
113
|
+
file,
|
|
114
|
+
source: dir,
|
|
115
|
+
fullPath: path.join(dirPath, file)
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return Array.from(fileMap.values());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Find all MCP configs for a specific tool in hierarchy
|
|
126
|
+
*/
|
|
127
|
+
function findAllConfigsForTool(toolId, startDir = null) {
|
|
128
|
+
const tool = TOOL_PATHS[toolId];
|
|
129
|
+
if (!tool) return [];
|
|
130
|
+
|
|
131
|
+
const dir = startDir || findProjectRoot() || process.cwd();
|
|
132
|
+
const homeDir = process.env.HOME || '';
|
|
133
|
+
const configs = [];
|
|
134
|
+
|
|
135
|
+
let currentDir = dir;
|
|
136
|
+
const root = path.parse(currentDir).root;
|
|
137
|
+
|
|
138
|
+
while (currentDir && currentDir !== root && currentDir !== homeDir) {
|
|
139
|
+
const configPath = path.join(currentDir, tool.projectConfig || `${tool.projectFolder}/mcps.json`);
|
|
140
|
+
if (fs.existsSync(configPath)) {
|
|
141
|
+
configs.push({
|
|
142
|
+
dir: currentDir,
|
|
143
|
+
configPath,
|
|
144
|
+
type: 'project'
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
currentDir = path.dirname(currentDir);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (tool.globalMcpConfig) {
|
|
151
|
+
const globalPath = tool.globalMcpConfig.replace(/^~/, homeDir);
|
|
152
|
+
if (fs.existsSync(globalPath)) {
|
|
153
|
+
configs.push({
|
|
154
|
+
dir: homeDir,
|
|
155
|
+
configPath: globalPath,
|
|
156
|
+
type: 'global'
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return configs.reverse();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = {
|
|
165
|
+
findProjectRoot,
|
|
166
|
+
findAllConfigs,
|
|
167
|
+
mergeConfigs,
|
|
168
|
+
getConfigPath,
|
|
169
|
+
collectFilesFromHierarchy,
|
|
170
|
+
findAllConfigsForTool,
|
|
171
|
+
};
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants and tool path configurations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const VERSION = '0.40.1';
|
|
6
|
+
|
|
7
|
+
// Tool-specific path configurations
|
|
8
|
+
const TOOL_PATHS = {
|
|
9
|
+
claude: {
|
|
10
|
+
name: 'Claude Code',
|
|
11
|
+
icon: 'sparkles',
|
|
12
|
+
color: 'orange',
|
|
13
|
+
globalConfig: '~/.claude/mcps.json',
|
|
14
|
+
globalSettings: '~/.claude/settings.json',
|
|
15
|
+
projectFolder: '.claude',
|
|
16
|
+
projectRules: '.claude/rules',
|
|
17
|
+
projectCommands: '.claude/commands',
|
|
18
|
+
projectWorkflows: '.claude/workflows',
|
|
19
|
+
projectInstructions: 'CLAUDE.md',
|
|
20
|
+
outputFile: '.mcp.json',
|
|
21
|
+
supportsEnvInterpolation: true,
|
|
22
|
+
},
|
|
23
|
+
gemini: {
|
|
24
|
+
name: 'Gemini CLI',
|
|
25
|
+
icon: 'terminal',
|
|
26
|
+
color: 'blue',
|
|
27
|
+
globalConfig: '~/.gemini/settings.json',
|
|
28
|
+
globalSettings: '~/.gemini/settings.json',
|
|
29
|
+
globalMcpConfig: '~/.gemini/mcps.json',
|
|
30
|
+
projectFolder: '.gemini',
|
|
31
|
+
projectConfig: '.gemini/mcps.json',
|
|
32
|
+
projectRules: '.gemini',
|
|
33
|
+
projectCommands: '.gemini/commands',
|
|
34
|
+
projectInstructions: 'GEMINI.md',
|
|
35
|
+
outputFile: '~/.gemini/settings.json',
|
|
36
|
+
supportsEnvInterpolation: true,
|
|
37
|
+
mergeIntoSettings: true,
|
|
38
|
+
},
|
|
39
|
+
antigravity: {
|
|
40
|
+
name: 'Antigravity',
|
|
41
|
+
icon: 'rocket',
|
|
42
|
+
color: 'purple',
|
|
43
|
+
globalConfig: '~/.gemini/antigravity/mcp_config.json',
|
|
44
|
+
globalMcpConfig: '~/.gemini/antigravity/mcps.json',
|
|
45
|
+
globalRules: '~/.gemini/GEMINI.md',
|
|
46
|
+
projectFolder: '.agent',
|
|
47
|
+
projectConfig: '.agent/mcps.json',
|
|
48
|
+
projectRules: '.agent/rules',
|
|
49
|
+
projectInstructions: 'GEMINI.md',
|
|
50
|
+
outputFile: '~/.gemini/antigravity/mcp_config.json',
|
|
51
|
+
supportsEnvInterpolation: false,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
module.exports = { VERSION, TOOL_PATHS };
|
package/lib/env.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variable commands
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* List environment variables
|
|
10
|
+
*/
|
|
11
|
+
function envList(projectDir = process.cwd()) {
|
|
12
|
+
const envPath = path.join(projectDir, '.claude', '.env');
|
|
13
|
+
|
|
14
|
+
console.log(`\nš Environment Variables (${projectDir}/.claude/.env)\n`);
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(envPath)) {
|
|
17
|
+
console.log(' No .env file found.');
|
|
18
|
+
console.log(' Create with: claude-config env set <KEY> <value>\n');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
23
|
+
const lines = content.split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
24
|
+
|
|
25
|
+
if (lines.length === 0) {
|
|
26
|
+
console.log(' No variables set.\n');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const [key] = line.split('=');
|
|
32
|
+
if (key) {
|
|
33
|
+
console.log(` ${key}=****`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
console.log(`\n Total: ${lines.length} variable(s)\n`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set environment variable
|
|
41
|
+
*/
|
|
42
|
+
function envSet(key, value, projectDir = process.cwd()) {
|
|
43
|
+
if (!key || value === undefined) {
|
|
44
|
+
console.error('Usage: claude-config env set <KEY> <value>');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const claudeDir = path.join(projectDir, '.claude');
|
|
49
|
+
const envPath = path.join(claudeDir, '.env');
|
|
50
|
+
|
|
51
|
+
if (!fs.existsSync(claudeDir)) {
|
|
52
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let lines = [];
|
|
56
|
+
if (fs.existsSync(envPath)) {
|
|
57
|
+
lines = fs.readFileSync(envPath, 'utf8').split('\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const keyUpper = key.toUpperCase();
|
|
61
|
+
let found = false;
|
|
62
|
+
lines = lines.map(line => {
|
|
63
|
+
if (line.startsWith(`${keyUpper}=`)) {
|
|
64
|
+
found = true;
|
|
65
|
+
return `${keyUpper}=${value}`;
|
|
66
|
+
}
|
|
67
|
+
return line;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!found) {
|
|
71
|
+
lines.push(`${keyUpper}=${value}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fs.writeFileSync(envPath, lines.filter(l => l.trim()).join('\n') + '\n');
|
|
75
|
+
|
|
76
|
+
console.log(`ā Set ${keyUpper} in .claude/.env`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Unset environment variable
|
|
81
|
+
*/
|
|
82
|
+
function envUnset(key, projectDir = process.cwd()) {
|
|
83
|
+
if (!key) {
|
|
84
|
+
console.error('Usage: claude-config env unset <KEY>');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const envPath = path.join(projectDir, '.claude', '.env');
|
|
89
|
+
|
|
90
|
+
if (!fs.existsSync(envPath)) {
|
|
91
|
+
console.log('No .env file found.');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const keyUpper = key.toUpperCase();
|
|
96
|
+
let lines = fs.readFileSync(envPath, 'utf8').split('\n');
|
|
97
|
+
const originalLength = lines.length;
|
|
98
|
+
|
|
99
|
+
lines = lines.filter(line => !line.startsWith(`${keyUpper}=`));
|
|
100
|
+
|
|
101
|
+
if (lines.length === originalLength) {
|
|
102
|
+
console.log(`Variable ${keyUpper} not found.`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fs.writeFileSync(envPath, lines.filter(l => l.trim()).join('\n') + '\n');
|
|
107
|
+
console.log(`ā Removed ${keyUpper} from .claude/.env`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
envList,
|
|
112
|
+
envSet,
|
|
113
|
+
envUnset,
|
|
114
|
+
};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Config Library - Modular exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
// Constants
|
|
7
|
+
...require('./constants'),
|
|
8
|
+
|
|
9
|
+
// Utils
|
|
10
|
+
...require('./utils'),
|
|
11
|
+
|
|
12
|
+
// Config
|
|
13
|
+
...require('./config'),
|
|
14
|
+
|
|
15
|
+
// Templates
|
|
16
|
+
...require('./templates'),
|
|
17
|
+
|
|
18
|
+
// Apply
|
|
19
|
+
...require('./apply'),
|
|
20
|
+
|
|
21
|
+
// MCPs
|
|
22
|
+
...require('./mcps'),
|
|
23
|
+
|
|
24
|
+
// Registry
|
|
25
|
+
...require('./registry'),
|
|
26
|
+
|
|
27
|
+
// Init
|
|
28
|
+
...require('./init'),
|
|
29
|
+
|
|
30
|
+
// Memory
|
|
31
|
+
...require('./memory'),
|
|
32
|
+
|
|
33
|
+
// Env
|
|
34
|
+
...require('./env'),
|
|
35
|
+
|
|
36
|
+
// Projects
|
|
37
|
+
...require('./projects'),
|
|
38
|
+
|
|
39
|
+
// Workstreams
|
|
40
|
+
...require('./workstreams'),
|
|
41
|
+
|
|
42
|
+
// Activity
|
|
43
|
+
...require('./activity'),
|
|
44
|
+
|
|
45
|
+
// CLI
|
|
46
|
+
...require('./cli'),
|
|
47
|
+
};
|