coder-config 0.40.1

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