@ngcodes/ccpm 0.1.0

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 (5) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +286 -0
  3. package/bin/ccpm +26 -0
  4. package/install.js +128 -0
  5. package/package.json +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nitin Gupta
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
+ # ccpm
2
+
3
+ **Claude Code Profile Manager** — run multiple Claude Code accounts simultaneously with full isolation.
4
+
5
+ [![CI](https://github.com/nitin-1926/claude-code-profile-manager/actions/workflows/ci.yml/badge.svg)](https://github.com/nitin-1926/claude-code-profile-manager/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+ [![Go Report Card](https://goreportcard.com/badge/github.com/nitin-1926/ccpm)](https://goreportcard.com/report/github.com/nitin-1926/ccpm)
8
+
9
+ ---
10
+
11
+ ## Privacy & Security
12
+
13
+ **ccpm is 100% local.** Your data never leaves your machine.
14
+
15
+ - No telemetry, analytics, or tracking of any kind
16
+ - No network calls — ccpm never contacts any server
17
+ - No data collection — we don't know you exist
18
+ - Credentials stored in your **OS keychain** (macOS Keychain, Linux Secret Service, Windows Credential Manager)
19
+ - Vault backups use **AES-256-GCM encryption** with a master key in your keychain
20
+ - Config files live in `~/.ccpm/` on your filesystem — nowhere else
21
+ - Fully open source — [audit the code yourself](https://github.com/nitin-1926/claude-code-profile-manager)
22
+
23
+ ---
24
+
25
+ ## The Problem
26
+
27
+ You have multiple Claude Code accounts — personal, work, side projects — and switching between them is painful:
28
+
29
+ - Manual logout/login cycles break your flow
30
+ - No way to run two accounts in parallel
31
+ - Settings, MCP servers, and memory bleed across accounts
32
+ - VS Code extension hardcoded to one account
33
+
34
+ **ccpm** fixes this. One command per terminal, full isolation, zero conflicts.
35
+
36
+ ## Quick Start
37
+
38
+ ```bash
39
+ # Install
40
+ curl -fsSL https://raw.githubusercontent.com/nitin-1926/claude-code-profile-manager/main/scripts/install.sh | sh
41
+
42
+ # Create profiles
43
+ ccpm add personal # Choose OAuth or API key — first profile auto-sets as default
44
+ ccpm add work # Each profile is fully isolated
45
+
46
+ # Run in parallel — one per terminal
47
+ ccpm run personal # Terminal 1
48
+ ccpm run work # Terminal 2
49
+ ```
50
+
51
+ That's it. Each terminal runs a completely isolated Claude Code instance with its own credentials, settings, and memory.
52
+
53
+ ## How It Works
54
+
55
+ ccpm is built on one official Claude Code mechanism:
56
+
57
+ > Setting `CLAUDE_CONFIG_DIR` to a directory path causes Claude Code to read all credentials, settings, MCP config, memory, and project data from that directory instead of `~/.claude`.
58
+
59
+ ccpm manages isolated directories under `~/.ccpm/profiles/<name>/` and launches Claude with the correct environment. Each profile gets its own keychain entry on macOS, its own credentials file on Linux/Windows, and its own config.
60
+
61
+ ```
62
+ ~/.ccpm/
63
+ ├── config.json # Global ccpm config
64
+ ├── profiles/
65
+ │ ├── personal/ # CLAUDE_CONFIG_DIR for "personal"
66
+ │ │ ├── .claude.json # Account data, settings
67
+ │ │ ├── settings.json # MCP servers, preferences
68
+ │ │ └── sessions/ # Chat history
69
+ │ └── work/ # Fully isolated from "personal"
70
+ └── vault/
71
+ ├── personal.enc # Encrypted credential backup
72
+ └── work.enc
73
+ ```
74
+
75
+ ## Installation
76
+
77
+ ### curl (macOS / Linux)
78
+
79
+ ```bash
80
+ curl -fsSL https://raw.githubusercontent.com/nitin-1926/claude-code-profile-manager/main/scripts/install.sh | sh
81
+ ```
82
+
83
+ ### Go
84
+
85
+ ```bash
86
+ go install github.com/nitin-1926/ccpm@latest
87
+ ```
88
+
89
+ ### npm
90
+
91
+ ```bash
92
+ npm install -g ccpm
93
+ ```
94
+
95
+ ### From source
96
+
97
+ ```bash
98
+ git clone https://github.com/nitin-1926/claude-code-profile-manager.git
99
+ cd claude-code-profile-manager/cli
100
+ make build # Binary at ./bin/ccpm
101
+ go install . # Install to $GOPATH/bin
102
+ ```
103
+
104
+ ## Shell Integration (Optional)
105
+
106
+ For `ccpm use` (setting a profile for your whole shell session), add this to your `~/.zshrc` or `~/.bashrc`:
107
+
108
+ ```bash
109
+ eval "$(ccpm shell-init)"
110
+ ```
111
+
112
+ Then reload: `source ~/.zshrc`
113
+
114
+ > **Note:** `ccpm run` works without any shell setup. Shell integration is only needed for `ccpm use`.
115
+
116
+ ## Commands
117
+
118
+ ### Profile Management
119
+
120
+ ```bash
121
+ ccpm add <name> # Create profile (OAuth or API key)
122
+ ccpm list # List all profiles with auth status
123
+ ccpm remove <name> # Delete a profile
124
+ ccpm status # Full system overview
125
+ ```
126
+
127
+ ### Running Claude
128
+
129
+ ```bash
130
+ ccpm run <name> # Launch Claude with this profile (recommended)
131
+ ccpm use <name> # Set profile for current shell session
132
+ ```
133
+
134
+ ### Authentication
135
+
136
+ ```bash
137
+ ccpm auth status # Check auth health across all profiles
138
+ ccpm auth refresh <n> # Re-authenticate a profile
139
+ ccpm auth backup <n> # Encrypted credential backup to vault
140
+ ccpm auth restore <n> # Restore credentials from vault backup
141
+ ```
142
+
143
+ ### IDE / VS Code
144
+
145
+ ```bash
146
+ ccpm set-default <name> # Set profile for VS Code extension
147
+ ccpm unset-default # Clear default
148
+ ```
149
+
150
+ ### Uninstall
151
+
152
+ ```bash
153
+ ccpm uninstall # Remove all ccpm data, profiles, and keychain entries
154
+ ```
155
+
156
+ ## Auth Methods
157
+
158
+ ccpm supports both authentication methods:
159
+
160
+ ### OAuth (Browser Login)
161
+
162
+ ```bash
163
+ $ ccpm add personal
164
+ Choose authentication method:
165
+ 1) OAuth (browser login via claude /login)
166
+ 2) API Key
167
+ Enter choice [1/2]: 1
168
+
169
+ Claude Code will launch. Run /login to authenticate.
170
+ # ...browser auth flow...
171
+
172
+ ✓ Profile "personal" authenticated via OAuth
173
+ ✓ Set as default profile (first profile)
174
+ ```
175
+
176
+ ### API Key
177
+
178
+ ```bash
179
+ $ ccpm add work
180
+ Choose authentication method:
181
+ 1) OAuth (browser login via claude /login)
182
+ 2) API Key
183
+ Enter choice [1/2]: 2
184
+
185
+ Enter your Anthropic API key: ****
186
+ ✓ Profile "work" authenticated via API key
187
+ ```
188
+
189
+ API keys are stored in your OS keychain (macOS Keychain / Linux Secret Service / Windows Credential Manager) — never in plaintext files.
190
+
191
+ ## Parallel Sessions
192
+
193
+ Run different accounts simultaneously in different terminals:
194
+
195
+ ```
196
+ ┌─────────────────────┐ ┌─────────────────────┐
197
+ │ Terminal 1 │ │ Terminal 2 │
198
+ │ │ │ │
199
+ │ $ ccpm run personal │ │ $ ccpm run work │
200
+ │ │ │ │
201
+ │ Claude Code │ │ Claude Code │
202
+ │ (personal@gmail.com) │ │ (work@company.com) │
203
+ │ │ │ │
204
+ │ Own settings │ │ Own settings │
205
+ │ Own MCP servers │ │ Own MCP servers │
206
+ │ Own memory │ │ Own memory │
207
+ └─────────────────────┘ └─────────────────────┘
208
+ ```
209
+
210
+ ## Platform Support
211
+
212
+ | Feature | macOS | Linux | Windows |
213
+ |---------|-------|-------|---------|
214
+ | OAuth auth | Keychain (per-profile isolation) | `.credentials.json` in profile dir | `.credentials.json` in profile dir |
215
+ | API key auth | Keychain | Secret Service (D-Bus) | Credential Manager |
216
+ | Parallel sessions | Yes | Yes | Yes |
217
+ | Vault backup | Yes | Yes | Yes |
218
+ | Shell hook | zsh, bash, fish | zsh, bash, fish | PowerShell |
219
+
220
+ ## Known Limitations
221
+
222
+ We believe in being honest about constraints:
223
+
224
+ | # | Limitation | Severity | Workaround |
225
+ |---|---|---|---|
226
+ | L1 | **VS Code extension ignores `CLAUDE_CONFIG_DIR`** — reads from `~/.claude` always | High | Use `ccpm set-default <profile>` to set the VS Code account |
227
+ | L2 | **`CLAUDE_CONFIG_DIR` path with `~/`** — Claude has a bug resolving `~/` paths on Linux | Medium | ccpm always uses absolute paths (handled automatically) |
228
+ | L3 | **Same-account parallel sessions** — running one profile in two terminals hits Anthropic's refresh token race | Medium | Use different profiles in different terminals |
229
+ | L4 | **Headless Linux** — `go-keyring` requires D-Bus + secret service | Low | API key profiles need a running secret service |
230
+
231
+ ## Project Structure
232
+
233
+ ```
234
+ claude-code-profile-manager/
235
+ ├── cli/ # Go source code (ccpm binary)
236
+ │ ├── cmd/ # CLI commands
237
+ │ ├── internal/ # Core packages (config, profile, vault, etc.)
238
+ │ └── Makefile
239
+ ├── docs/ # Documentation website (Next.js, deploy to Vercel)
240
+ ├── npm/ # npm wrapper package
241
+ ├── scripts/ # Install script
242
+ └── .github/ # CI/CD workflows
243
+ ```
244
+
245
+ ## Roadmap
246
+
247
+ - [x] Profile management (add, list, remove, run)
248
+ - [x] OAuth + API key authentication
249
+ - [x] Encrypted vault backup/restore
250
+ - [x] Shell integration (use, shell-init)
251
+ - [x] VS Code default profile (set-default)
252
+ - [x] Uninstall command
253
+ - [ ] Per-profile MCP server management
254
+ - [ ] Shared vs isolated config (skills, commands, CLAUDE.md)
255
+ - [ ] Token optimization presets
256
+ - [ ] Diagnostics (`ccpm doctor`)
257
+ - [ ] GUI companion app
258
+ - [ ] Multi-tool support (Codex CLI, Cursor CLI, Gemini CLI)
259
+
260
+ ## Contributing
261
+
262
+ Contributions welcome! Please open an issue first to discuss what you'd like to change.
263
+
264
+ ```bash
265
+ # Development setup
266
+ git clone https://github.com/nitin-1926/claude-code-profile-manager.git
267
+ cd claude-code-profile-manager/cli
268
+ go mod tidy
269
+ make build # Build binary to ./bin/ccpm
270
+ go install . # Install to $GOPATH/bin for testing
271
+ make test # Run tests
272
+ ```
273
+
274
+ ### Testing the npm package locally
275
+
276
+ ```bash
277
+ cd npm
278
+ npm pack # Creates ccpm-0.1.0.tgz
279
+ npm install -g ccpm-0.1.0.tgz # Install from local tarball
280
+ ccpm --version # Verify
281
+ npm uninstall -g ccpm # Clean up
282
+ ```
283
+
284
+ ## License
285
+
286
+ [MIT](LICENSE)
package/bin/ccpm ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ // JS shim that execs the native ccpm binary downloaded by install.js
3
+ const { spawnSync } = require("child_process");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+
7
+ const isWin = process.platform === "win32";
8
+ const binary = path.join(__dirname, isWin ? "ccpm-native.exe" : "ccpm-native");
9
+
10
+ if (!fs.existsSync(binary)) {
11
+ console.error("ccpm: native binary not found at " + binary);
12
+ console.error(
13
+ "The postinstall step may have failed. Reinstall with `npm i -g @ngcodes/ccpm`"
14
+ );
15
+ console.error(
16
+ "Or download manually: https://github.com/nitin-1926/claude-code-profile-manager/releases"
17
+ );
18
+ process.exit(1);
19
+ }
20
+
21
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
22
+ if (result.error) {
23
+ console.error("ccpm: failed to launch native binary:", result.error.message);
24
+ process.exit(1);
25
+ }
26
+ process.exit(result.status ?? 1);
package/install.js ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ // Downloads the correct ccpm binary for the current platform during npm install
3
+
4
+ const { execSync } = require("child_process");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const https = require("https");
8
+
9
+ const REPO = "nitin-1926/claude-code-profile-manager";
10
+ const BINARY = "ccpm";
11
+
12
+ function getPlatform() {
13
+ const platform = process.platform;
14
+ const arch = process.arch;
15
+
16
+ const osMap = { darwin: "darwin", linux: "linux", win32: "windows" };
17
+ const archMap = { x64: "amd64", arm64: "arm64" };
18
+
19
+ const os = osMap[platform];
20
+ const cpu = archMap[arch];
21
+
22
+ if (!os || !cpu) {
23
+ console.error(`Unsupported platform: ${platform}/${arch}`);
24
+ process.exit(1);
25
+ }
26
+
27
+ return { os, arch: cpu };
28
+ }
29
+
30
+ function getLatestVersion() {
31
+ return new Promise((resolve, reject) => {
32
+ const url = `https://api.github.com/repos/${REPO}/releases/latest`;
33
+ https.get(url, { headers: { "User-Agent": "ccpm-npm" } }, (res) => {
34
+ let data = "";
35
+ res.on("data", (chunk) => (data += chunk));
36
+ res.on("end", () => {
37
+ try {
38
+ const json = JSON.parse(data);
39
+ resolve(json.tag_name.replace(/^v/, ""));
40
+ } catch {
41
+ // Fallback to package.json version
42
+ const pkg = require("./package.json");
43
+ resolve(pkg.version);
44
+ }
45
+ });
46
+ res.on("error", reject);
47
+ });
48
+ });
49
+ }
50
+
51
+ async function download(url, dest) {
52
+ return new Promise((resolve, reject) => {
53
+ const follow = (url) => {
54
+ https.get(url, { headers: { "User-Agent": "ccpm-npm" } }, (res) => {
55
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
56
+ follow(res.headers.location);
57
+ return;
58
+ }
59
+ if (res.statusCode !== 200) {
60
+ reject(new Error(`Download failed: HTTP ${res.statusCode}`));
61
+ return;
62
+ }
63
+ const file = fs.createWriteStream(dest);
64
+ res.pipe(file);
65
+ file.on("finish", () => { file.close(); resolve(); });
66
+ file.on("error", reject);
67
+ });
68
+ };
69
+ follow(url);
70
+ });
71
+ }
72
+
73
+ async function main() {
74
+ const { os, arch } = getPlatform();
75
+ const version = await getLatestVersion();
76
+
77
+ const ext = os === "windows" ? "zip" : "tar.gz";
78
+ const url = `https://github.com/${REPO}/releases/download/v${version}/${BINARY}_${os}_${arch}.${ext}`;
79
+
80
+ console.log(`Installing ccpm v${version} for ${os}/${arch}...`);
81
+
82
+ const binDir = path.join(__dirname, "bin");
83
+ fs.mkdirSync(binDir, { recursive: true });
84
+
85
+ // Extract into a temp subdirectory so the archive's `ccpm` binary doesn't
86
+ // overwrite the JS shim that npm uses to create the global `ccpm` command.
87
+ const tmpDir = path.join(binDir, ".extract");
88
+ if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true, force: true });
89
+ fs.mkdirSync(tmpDir, { recursive: true });
90
+
91
+ const archivePath = path.join(tmpDir, `archive.${ext}`);
92
+ await download(url, archivePath);
93
+
94
+ if (ext === "zip") {
95
+ execSync(`unzip -o -q "${archivePath}" -d "${tmpDir}"`, { stdio: "inherit" });
96
+ } else {
97
+ execSync(`tar -xzf "${archivePath}" -C "${tmpDir}"`, { stdio: "inherit" });
98
+ }
99
+
100
+ // Move the native binary to bin/ccpm-native (or ccpm-native.exe on Windows).
101
+ // The JS shim at bin/ccpm execs this at runtime.
102
+ const extractedName = os === "windows" ? `${BINARY}.exe` : BINARY;
103
+ const nativeName = os === "windows" ? "ccpm-native.exe" : "ccpm-native";
104
+ const extractedPath = path.join(tmpDir, extractedName);
105
+ const nativePath = path.join(binDir, nativeName);
106
+
107
+ if (!fs.existsSync(extractedPath)) {
108
+ throw new Error(`extracted binary not found at ${extractedPath}`);
109
+ }
110
+
111
+ if (fs.existsSync(nativePath)) fs.unlinkSync(nativePath);
112
+ fs.renameSync(extractedPath, nativePath);
113
+
114
+ if (os !== "windows") {
115
+ fs.chmodSync(nativePath, 0o755);
116
+ }
117
+
118
+ // Clean up temp dir
119
+ fs.rmSync(tmpDir, { recursive: true, force: true });
120
+
121
+ console.log(`ccpm v${version} installed successfully!`);
122
+ }
123
+
124
+ main().catch((err) => {
125
+ console.error("Failed to install ccpm:", err.message);
126
+ console.error("You can install manually: https://github.com/" + REPO + "/releases");
127
+ process.exit(1);
128
+ });
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@ngcodes/ccpm",
3
+ "version": "0.1.0",
4
+ "description": "Claude Code Profile Manager — manage multiple Claude Code accounts with isolated profiles",
5
+ "bin": {
6
+ "ccpm": "bin/ccpm"
7
+ },
8
+ "files": [
9
+ "install.js",
10
+ "bin/ccpm",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "scripts": {
18
+ "postinstall": "node install.js",
19
+ "test-local": "echo 'To test locally: npm pack && npm install -g nitin-1926-ccpm-0.1.0.tgz'"
20
+ },
21
+ "keywords": [
22
+ "claude",
23
+ "claude-code",
24
+ "profile-manager",
25
+ "multi-account",
26
+ "anthropic",
27
+ "cli"
28
+ ],
29
+ "author": "Nitin Gupta",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/nitin-1926/claude-code-profile-manager.git"
34
+ },
35
+ "homepage": "https://github.com/nitin-1926/claude-code-profile-manager",
36
+ "bugs": {
37
+ "url": "https://github.com/nitin-1926/claude-code-profile-manager/issues"
38
+ },
39
+ "os": ["darwin", "linux", "win32"],
40
+ "cpu": ["x64", "arm64"],
41
+ "engines": {
42
+ "node": ">=16"
43
+ }
44
+ }