multi-ccp 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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial TypeScript npm package scaffold.
6
+ - Added cross-platform profile core for API and Claude login profiles.
7
+ - Added CLI commands for `list`, `add`, `add-login`, `remove`, `status`, `path`, `edit`, and `start`.
8
+ - Added Vitest coverage for core profile behavior.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
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,104 @@
1
+ # multi-ccp
2
+
3
+ Cross-platform Claude Code profile manager for multiple API profiles, separate Claude login accounts, and future Claude Code Router workflows.
4
+
5
+ The command name is `ccp`.
6
+
7
+ ## Goals
8
+
9
+ - Manage multiple Claude Code config directories through `CLAUDE_CONFIG_DIR`.
10
+ - Support API profiles with `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, and model env.
11
+ - Support login profiles that keep separate Claude Code account login state without storing account passwords.
12
+ - Run on Windows, macOS, and Linux from one npm package.
13
+ - Keep the core profile logic reusable for a future framework-free local Web UI.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install -g multi-ccp
19
+ ```
20
+
21
+ For local development:
22
+
23
+ ```bash
24
+ npm install
25
+ npm run build
26
+ npm run dev -- help
27
+ ```
28
+
29
+ ## Commands
30
+
31
+ ```bash
32
+ ccp list
33
+ ccp add <profile>
34
+ ccp add-login <profile>
35
+ ccp remove <profile>
36
+ ccp status <profile|main>
37
+ ccp start <profile> [claude args...]
38
+ ccp path <profile|main>
39
+ ccp edit <profile>
40
+ ```
41
+
42
+ ## Profile Types
43
+
44
+ ### API profile
45
+
46
+ ```bash
47
+ ccp add kimi
48
+ ccp start kimi
49
+ ```
50
+
51
+ Creates `~/.claude-profiles/kimi/settings.json` with Anthropic-compatible API environment variables.
52
+
53
+ ### Login profile
54
+
55
+ ```bash
56
+ ccp add-login work
57
+ ccp start work
58
+ ```
59
+
60
+ Creates a profile without `ANTHROPIC_BASE_URL` or `ANTHROPIC_AUTH_TOKEN`. Claude Code stores that account's login state under the profile config directory when you complete the normal Claude Code login flow.
61
+
62
+ Create another login account with:
63
+
64
+ ```bash
65
+ ccp add-login personal
66
+ ccp start personal
67
+ ```
68
+
69
+ Each profile gets its own `CLAUDE_CONFIG_DIR`.
70
+
71
+ ## Current Scope
72
+
73
+ This TypeScript npm version currently implements the cross-platform profile manager MVP:
74
+
75
+ - `list`
76
+ - `add`
77
+ - `add-login`
78
+ - `remove`
79
+ - `status`
80
+ - `path`
81
+ - `edit`
82
+ - `start`
83
+
84
+ Planned migrations from the legacy PowerShell tool:
85
+
86
+ - `add-ccr`
87
+ - `ccp ccr status|install|start|stop|restart|ui|model`
88
+ - `sync-session`
89
+ - `ccp ui`
90
+
91
+ The future Web UI should use vanilla browser APIs or Web Components. Vue and React are intentionally out of scope for the runtime UI. Icon libraries such as Lucide are acceptable.
92
+
93
+ ## Development
94
+
95
+ ```bash
96
+ npm install
97
+ npm run typecheck
98
+ npm test
99
+ npm run build
100
+ ```
101
+
102
+ ## License
103
+
104
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import { createProgram } from "./program.js";
3
+ import { CcpError } from "../core/errors.js";
4
+ try {
5
+ await createProgram().parseAsync(process.argv);
6
+ }
7
+ catch (error) {
8
+ if (error instanceof CcpError) {
9
+ console.error(`ccp: ${error.message}`);
10
+ process.exit(1);
11
+ }
12
+ throw error;
13
+ }
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,IAAI,CAAC;IACH,MAAM,aAAa,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,KAAK,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function createProgram(): Command;
@@ -0,0 +1,196 @@
1
+ import { confirm, input, password } from "@inquirer/prompts";
2
+ import { Command } from "commander";
3
+ import { CcpError } from "../core/errors.js";
4
+ import { launchClaude } from "../core/launcher.js";
5
+ import { resolveConfigDir } from "../core/profiles.js";
6
+ import { createApiProfile, createLoginProfile, listProfiles, removeProfile, summarizeProfile } from "../core/profiles.js";
7
+ import { getSettingsPath } from "../core/settings.js";
8
+ import { getCcrStatusPlaceholder } from "../core/ccr.js";
9
+ import { openEditor } from "../platform/editor.js";
10
+ function printProfile(profile) {
11
+ if (profile.model) {
12
+ console.log(`${profile.name}\t${profile.model}\t${profile.baseUrl}`);
13
+ }
14
+ else {
15
+ console.log(`${profile.name}\t${profile.baseUrl}`);
16
+ }
17
+ }
18
+ async function showStatus(name) {
19
+ const config = await resolveConfigDir(name, { allowMain: true });
20
+ const profile = await summarizeProfile(config.name, config.dir);
21
+ console.log(`Profile: ${profile.name}`);
22
+ console.log(`Path: ${profile.dir}`);
23
+ console.log(`Settings: ${profile.settingsPath}`);
24
+ if (profile.meta) {
25
+ console.log(`Type: ${profile.meta.type}`);
26
+ if (profile.meta.endpoint)
27
+ console.log(`Endpoint: ${profile.meta.endpoint}`);
28
+ if (profile.meta.autoStart !== undefined)
29
+ console.log(`AutoStart: ${profile.meta.autoStart}`);
30
+ if (profile.meta.ccrPreset)
31
+ console.log(`CCR Preset: ${profile.meta.ccrPreset}`);
32
+ if (profile.meta.ccrRoute)
33
+ console.log(`CCR Route: ${profile.meta.ccrRoute}`);
34
+ }
35
+ else {
36
+ console.log(`Type: ${profile.type}`);
37
+ }
38
+ if (profile.type === "login") {
39
+ console.log("Auth: Claude Code login");
40
+ console.log("Env: missing");
41
+ return;
42
+ }
43
+ if (!profile.baseUrl && !profile.model && profile.tokenStatus === "missing") {
44
+ console.log("Env: missing");
45
+ return;
46
+ }
47
+ console.log(`Base: ${profile.baseUrl}`);
48
+ if (profile.type === "ccr" && !profile.model) {
49
+ console.log("Model: (routed by CCR preset)");
50
+ }
51
+ else {
52
+ console.log(`Model: ${profile.model}`);
53
+ }
54
+ console.log(`Token: ${profile.tokenStatus}`);
55
+ }
56
+ export function createProgram() {
57
+ const program = new Command();
58
+ program
59
+ .name("ccp")
60
+ .description("Claude Code profile manager")
61
+ .version("0.1.0");
62
+ program
63
+ .command("list")
64
+ .description("List Claude Code profiles")
65
+ .action(async () => {
66
+ const profiles = await listProfiles();
67
+ if (profiles.length === 0) {
68
+ console.log("No profiles found.");
69
+ return;
70
+ }
71
+ profiles.forEach(printProfile);
72
+ });
73
+ program
74
+ .command("add")
75
+ .argument("<profile>")
76
+ .description("Create an API profile with base URL, token, and model")
77
+ .action(async (profile) => {
78
+ console.log(`Create Claude API profile: ${profile}`);
79
+ const baseUrl = await input({ message: "ANTHROPIC_BASE_URL", required: true });
80
+ const token = await password({ message: "ANTHROPIC_AUTH_TOKEN (hidden, Enter to leave placeholder)", mask: "*" });
81
+ const model = await input({ message: "Model", required: true });
82
+ const ok = await confirm({ message: "Create this profile?", default: true });
83
+ if (!ok) {
84
+ console.log("Cancelled.");
85
+ return;
86
+ }
87
+ const created = await createApiProfile({ name: profile, baseUrl, token, model });
88
+ console.log(`Created profile '${created.name}'.`);
89
+ console.log(`Run: ccp start ${created.name}`);
90
+ });
91
+ program
92
+ .command("add-login")
93
+ .argument("<profile>")
94
+ .description("Create a login profile that stores a separate Claude Code account login state")
95
+ .action(async (profile) => {
96
+ console.log(`Create Claude login profile: ${profile}`);
97
+ console.log("This profile will not set ANTHROPIC_BASE_URL or ANTHROPIC_AUTH_TOKEN.");
98
+ const ok = await confirm({ message: "Create this login profile?", default: true });
99
+ if (!ok) {
100
+ console.log("Cancelled.");
101
+ return;
102
+ }
103
+ const created = await createLoginProfile({ name: profile });
104
+ console.log(`Created Claude login profile '${created.name}'.`);
105
+ console.log(`Run: ccp start ${created.name}`);
106
+ console.log("Then complete the Claude Code login flow for this account.");
107
+ });
108
+ program
109
+ .command("remove")
110
+ .argument("<profile>")
111
+ .description("Delete a profile")
112
+ .action(async (profile) => {
113
+ const config = await resolveConfigDir(profile, { allowMain: false });
114
+ console.log(`This will permanently delete Claude profile '${profile}':`);
115
+ console.log(config.dir);
116
+ console.log("This cannot be undone. No backup will be created.");
117
+ const typed = await input({ message: `Type '${profile}' to delete` });
118
+ if (typed !== profile) {
119
+ console.log("Cancelled.");
120
+ return;
121
+ }
122
+ await removeProfile(profile);
123
+ console.log(`Deleted profile '${profile}'.`);
124
+ });
125
+ program
126
+ .command("status")
127
+ .argument("<profile>")
128
+ .description("Show profile status")
129
+ .action(showStatus);
130
+ program
131
+ .command("path")
132
+ .argument("<profile>")
133
+ .description("Print profile config path")
134
+ .action(async (profile) => {
135
+ const config = await resolveConfigDir(profile, { allowMain: true });
136
+ console.log(config.dir);
137
+ });
138
+ program
139
+ .command("edit")
140
+ .argument("<profile>")
141
+ .description("Open a profile settings file in your editor")
142
+ .action(async (profile) => {
143
+ const config = await resolveConfigDir(profile, { allowMain: false });
144
+ const code = await openEditor(getSettingsPath(config.dir));
145
+ process.exitCode = code;
146
+ });
147
+ program
148
+ .command("start")
149
+ .argument("<profile>")
150
+ .allowUnknownOption(true)
151
+ .argument("[claudeArgs...]", "Arguments passed through to Claude Code")
152
+ .description("Start Claude Code with a profile")
153
+ .action(async (profile, claudeArgs) => {
154
+ const code = await launchClaude({
155
+ name: profile,
156
+ claudeArgs,
157
+ confirmMainConfigCwd: async ({ currentDir, fallbackDir, profileName }) => {
158
+ console.log("");
159
+ console.log("Current directory is the Claude main config location:");
160
+ console.log(` ${currentDir}`);
161
+ console.log("");
162
+ console.log(`Claude Code may read project settings that override profile '${profileName}'.`);
163
+ console.log("");
164
+ console.log("If you continue, ccp will switch to this isolated workdir:");
165
+ console.log(` ${fallbackDir}`);
166
+ console.log("");
167
+ return confirm({ message: "Continue and switch workdir?", default: false });
168
+ }
169
+ });
170
+ process.exitCode = code;
171
+ });
172
+ program
173
+ .command("ccr")
174
+ .argument("[action]")
175
+ .description("Manage Claude Code Router integration")
176
+ .action(() => {
177
+ const status = getCcrStatusPlaceholder();
178
+ console.log(status.message);
179
+ process.exitCode = 1;
180
+ });
181
+ program
182
+ .command("sync-session")
183
+ .allowUnknownOption(true)
184
+ .description("Sync Claude Code sessions between profiles")
185
+ .action(() => {
186
+ throw new CcpError("sync-session is planned for a later TypeScript CLI release. Use the legacy PowerShell ccp for now.");
187
+ });
188
+ program
189
+ .command("ui")
190
+ .description("Start the local web UI")
191
+ .action(() => {
192
+ throw new CcpError("Web UI is planned. It will use vanilla web components and no Vue/React runtime.");
193
+ });
194
+ return program;
195
+ }
196
+ //# sourceMappingURL=program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export interface CcrStatus {
2
+ implemented: false;
3
+ message: string;
4
+ }
5
+ export declare function getCcrStatusPlaceholder(): CcrStatus;
@@ -0,0 +1,7 @@
1
+ export function getCcrStatusPlaceholder() {
2
+ return {
3
+ implemented: false,
4
+ message: "CCR management is planned for a later TypeScript CLI release. Use the legacy PowerShell ccp for CCR profiles for now."
5
+ };
6
+ }
7
+ //# sourceMappingURL=ccr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ccr.js","sourceRoot":"","sources":["../../src/core/ccr.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,uBAAuB;IACrC,OAAO;QACL,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,uHAAuH;KACjI,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare class CcpError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare function assertProfileName(name: string): void;
@@ -0,0 +1,18 @@
1
+ export class CcpError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "CcpError";
5
+ }
6
+ }
7
+ export function assertProfileName(name) {
8
+ if (!name.trim()) {
9
+ throw new CcpError("Missing profile name.");
10
+ }
11
+ if (name.toLowerCase() === "main") {
12
+ throw new CcpError("'main' is reserved and cannot be used as a profile name.");
13
+ }
14
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(name)) {
15
+ throw new CcpError(`Invalid profile name '${name}'. Use letters, numbers, underscore, or hyphen, and start with a letter or number.`);
16
+ }
17
+ }
18
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,QAAQ,CAAC,0DAA0D,CAAC,CAAC;IACjF,CAAC;IAED,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,QAAQ,CAChB,yBAAyB,IAAI,oFAAoF,CAClH,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type PathContext } from "./paths.js";
2
+ export interface LaunchOptions {
3
+ name: string;
4
+ claudeArgs?: string[];
5
+ context?: PathContext;
6
+ cwd?: string;
7
+ confirmMainConfigCwd?: (details: {
8
+ currentDir: string;
9
+ fallbackDir: string;
10
+ profileName: string;
11
+ }) => Promise<boolean>;
12
+ }
13
+ export declare function prepareClaudeLaunch(options: LaunchOptions): Promise<{
14
+ command: string;
15
+ args: string[];
16
+ cwd: string;
17
+ env: NodeJS.ProcessEnv;
18
+ }>;
19
+ export declare function launchClaude(options: LaunchOptions): Promise<number>;
@@ -0,0 +1,80 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { CcpError } from "./errors.js";
5
+ import { getHomeDir, getHomeWorkDir, getMainClaudeDir } from "./paths.js";
6
+ import { resolveConfigDir } from "./profiles.js";
7
+ import { readMeta } from "./settings.js";
8
+ const MODEL_ENV_NAMES = [
9
+ "ANTHROPIC_MODEL",
10
+ "ANTHROPIC_SMALL_FAST_MODEL",
11
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL",
12
+ "ANTHROPIC_DEFAULT_OPUS_MODEL",
13
+ "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME",
14
+ "ANTHROPIC_DEFAULT_SONNET_MODEL",
15
+ "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME"
16
+ ];
17
+ function normalizePath(value) {
18
+ return path.resolve(value).replace(/[\\/]+$/, "").toLowerCase();
19
+ }
20
+ function buildLaunchEnv(profileDir, profileName) {
21
+ const env = { ...process.env };
22
+ for (const name of MODEL_ENV_NAMES) {
23
+ delete env[name];
24
+ }
25
+ env.CLAUDE_CONFIG_DIR = profileDir;
26
+ env.CCP_PROFILE = profileName;
27
+ return env;
28
+ }
29
+ async function resolveLaunchCwd(options) {
30
+ const context = options.context ?? {};
31
+ const currentDir = options.cwd ?? context.cwd ?? process.cwd();
32
+ const homeDir = getHomeDir(context);
33
+ const mainDir = getMainClaudeDir(context);
34
+ if (normalizePath(currentDir) !== normalizePath(homeDir) && normalizePath(currentDir) !== normalizePath(mainDir)) {
35
+ return currentDir;
36
+ }
37
+ const fallbackDir = getHomeWorkDir(context);
38
+ const ok = await options.confirmMainConfigCwd?.({ currentDir, fallbackDir, profileName: options.name });
39
+ if (!ok) {
40
+ throw new CcpError("Cancelled.");
41
+ }
42
+ await mkdir(fallbackDir, { recursive: true });
43
+ return fallbackDir;
44
+ }
45
+ export async function prepareClaudeLaunch(options) {
46
+ const config = await resolveConfigDir(options.name, { allowMain: false, context: options.context });
47
+ const meta = await readMeta(config.dir);
48
+ if (meta?.type === "ccr") {
49
+ throw new CcpError("CCR profiles are not implemented in the TypeScript CLI yet. Use the legacy ccp.ps1 for this profile for now.");
50
+ }
51
+ return {
52
+ command: "claude",
53
+ args: options.claudeArgs ?? [],
54
+ cwd: await resolveLaunchCwd(options),
55
+ env: buildLaunchEnv(config.dir, options.name)
56
+ };
57
+ }
58
+ export async function launchClaude(options) {
59
+ const launch = await prepareClaudeLaunch(options);
60
+ return new Promise((resolve, reject) => {
61
+ const child = spawn(launch.command, launch.args, {
62
+ cwd: launch.cwd,
63
+ env: launch.env,
64
+ stdio: "inherit",
65
+ shell: process.platform === "win32"
66
+ });
67
+ child.on("error", (error) => {
68
+ reject(new CcpError(`Failed to start Claude Code: ${error.message}`));
69
+ });
70
+ child.on("exit", (code, signal) => {
71
+ if (signal) {
72
+ resolve(1);
73
+ }
74
+ else {
75
+ resolve(code ?? 0);
76
+ }
77
+ });
78
+ });
79
+ }
80
+ //# sourceMappingURL=launcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/core/launcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface PathContext {
2
+ homeDir?: string;
3
+ cwd?: string;
4
+ }
5
+ export declare function getHomeDir(context?: PathContext): string;
6
+ export declare function getProfilesRoot(context?: PathContext): string;
7
+ export declare function getMainClaudeDir(context?: PathContext): string;
8
+ export declare function getHomeWorkDir(context?: PathContext): string;
9
+ export declare function getClaudeCodeRouterDir(context?: PathContext): string;
10
+ export declare function getClaudeCodeRouterConfigPath(context?: PathContext): string;
11
+ export declare function getProjectKey(projectPath?: string): string;
12
+ export declare function getProjectDir(configDir: string, projectKey: string): string;
13
+ export declare function isWindows(): boolean;
@@ -0,0 +1,31 @@
1
+ import { homedir, platform } from "node:os";
2
+ import path from "node:path";
3
+ export function getHomeDir(context = {}) {
4
+ return context.homeDir ?? homedir();
5
+ }
6
+ export function getProfilesRoot(context = {}) {
7
+ return path.join(getHomeDir(context), ".claude-profiles");
8
+ }
9
+ export function getMainClaudeDir(context = {}) {
10
+ return path.join(getHomeDir(context), ".claude");
11
+ }
12
+ export function getHomeWorkDir(context = {}) {
13
+ return path.join(getProfilesRoot(context), ".workdirs", "home");
14
+ }
15
+ export function getClaudeCodeRouterDir(context = {}) {
16
+ return path.join(getHomeDir(context), ".claude-code-router");
17
+ }
18
+ export function getClaudeCodeRouterConfigPath(context = {}) {
19
+ return path.join(getClaudeCodeRouterDir(context), "config.json");
20
+ }
21
+ export function getProjectKey(projectPath = process.cwd()) {
22
+ const resolved = path.resolve(projectPath).replace(/[\\/]+$/, "");
23
+ return resolved.replace(/:/g, "-").replace(/[\\/]/g, "-");
24
+ }
25
+ export function getProjectDir(configDir, projectKey) {
26
+ return path.join(configDir, "projects", projectKey);
27
+ }
28
+ export function isWindows() {
29
+ return platform() === "win32";
30
+ }
31
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAO7B,MAAM,UAAU,UAAU,CAAC,UAAuB,EAAE;IAClD,OAAO,OAAO,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAuB,EAAE;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAuB,EAAE;IACxD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAuB,EAAE;IACtD,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAuB,EAAE;IAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,UAAuB,EAAE;IACrE,OAAO,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE;IACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,UAAkB;IACjE,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,QAAQ,EAAE,KAAK,OAAO,CAAC;AAChC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type PathContext } from "./paths.js";
2
+ import type { ClaudeSettings, CreateApiProfileInput, CreateLoginProfileInput, ProfileMeta, ProfileSummary, ProfileType } from "./types.js";
3
+ export declare function getProfileDir(name: string, context?: PathContext): string;
4
+ export declare function resolveConfigDir(name: string, options?: {
5
+ allowMain?: boolean;
6
+ context?: PathContext;
7
+ }): Promise<{
8
+ name: string;
9
+ dir: string;
10
+ isMain: boolean;
11
+ }>;
12
+ export declare function inferProfileType(settings?: ClaudeSettings, meta?: ProfileMeta): ProfileType;
13
+ export declare function summarizeProfile(name: string, dir: string): Promise<ProfileSummary>;
14
+ export declare function listProfiles(context?: PathContext): Promise<ProfileSummary[]>;
15
+ export declare function createApiProfile(input: CreateApiProfileInput, context?: PathContext): Promise<ProfileSummary>;
16
+ export declare function createLoginProfile(input: CreateLoginProfileInput, context?: PathContext): Promise<ProfileSummary>;
17
+ export declare function removeProfile(name: string, context?: PathContext): Promise<string>;
@@ -0,0 +1,141 @@
1
+ import { access, mkdir, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { assertProfileName, CcpError } from "./errors.js";
4
+ import { getMainClaudeDir, getProfilesRoot } from "./paths.js";
5
+ import { getSettingsPath, readMeta, readSettings, removeProfileDir, writeMeta, writeSettings } from "./settings.js";
6
+ async function exists(target) {
7
+ try {
8
+ await access(target);
9
+ return true;
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ export function getProfileDir(name, context = {}) {
16
+ return path.join(getProfilesRoot(context), name);
17
+ }
18
+ export async function resolveConfigDir(name, options = {}) {
19
+ const allowMain = options.allowMain ?? true;
20
+ const context = options.context ?? {};
21
+ if (!name.trim()) {
22
+ throw new CcpError("Missing profile name. Run 'ccp list' to see available profiles.");
23
+ }
24
+ if (allowMain && name.toLowerCase() === "main") {
25
+ const dir = getMainClaudeDir(context);
26
+ if (!(await exists(dir))) {
27
+ throw new CcpError(`Main Claude config directory does not exist: ${dir}`);
28
+ }
29
+ return { name: "main", dir, isMain: true };
30
+ }
31
+ const dir = getProfileDir(name, context);
32
+ if (!(await exists(dir))) {
33
+ throw new CcpError(`Profile '${name}' does not exist: ${dir}`);
34
+ }
35
+ return { name, dir, isMain: false };
36
+ }
37
+ export function inferProfileType(settings, meta) {
38
+ if (meta?.type) {
39
+ return meta.type;
40
+ }
41
+ if (settings?.env?.ANTHROPIC_BASE_URL || settings?.env?.ANTHROPIC_AUTH_TOKEN) {
42
+ return "api";
43
+ }
44
+ return "unknown";
45
+ }
46
+ export async function summarizeProfile(name, dir) {
47
+ const settings = await readSettings(dir);
48
+ const meta = await readMeta(dir);
49
+ const type = inferProfileType(settings, meta);
50
+ const env = settings?.env ?? {};
51
+ const token = env.ANTHROPIC_AUTH_TOKEN ?? "";
52
+ let model = env.ANTHROPIC_MODEL ?? "";
53
+ const baseUrl = env.ANTHROPIC_BASE_URL ?? "";
54
+ if (type === "ccr" && meta?.ccrRoute) {
55
+ model = `ccr:${meta.ccrRoute}`;
56
+ }
57
+ else if (type === "ccr") {
58
+ model = "ccr";
59
+ }
60
+ else if (type === "login") {
61
+ model = "login";
62
+ }
63
+ return {
64
+ name,
65
+ dir,
66
+ type,
67
+ baseUrl,
68
+ model,
69
+ tokenStatus: token && token !== "REPLACE_WITH_FULL_TOKEN" ? "set" : "missing",
70
+ settingsPath: getSettingsPath(dir),
71
+ meta
72
+ };
73
+ }
74
+ export async function listProfiles(context = {}) {
75
+ const root = getProfilesRoot(context);
76
+ if (!(await exists(root))) {
77
+ return [];
78
+ }
79
+ const entries = await readdir(root, { withFileTypes: true });
80
+ const profiles = [];
81
+ for (const entry of entries) {
82
+ if (!entry.isDirectory()) {
83
+ continue;
84
+ }
85
+ const dir = path.join(root, entry.name);
86
+ if (await exists(getSettingsPath(dir))) {
87
+ profiles.push(await summarizeProfile(entry.name, dir));
88
+ }
89
+ }
90
+ return profiles.sort((a, b) => a.name.localeCompare(b.name));
91
+ }
92
+ async function assertNewProfile(name, context) {
93
+ assertProfileName(name);
94
+ const profileDir = getProfileDir(name, context);
95
+ if (await exists(profileDir)) {
96
+ throw new CcpError(`Profile '${name}' already exists: ${profileDir}`);
97
+ }
98
+ return profileDir;
99
+ }
100
+ export async function createApiProfile(input, context = {}) {
101
+ const profileDir = await assertNewProfile(input.name, context);
102
+ if (!input.baseUrl.trim()) {
103
+ throw new CcpError("ANTHROPIC_BASE_URL is required.");
104
+ }
105
+ if (!input.model.trim()) {
106
+ throw new CcpError("Model is required.");
107
+ }
108
+ await mkdir(profileDir, { recursive: true });
109
+ await writeSettings(profileDir, {
110
+ theme: "dark",
111
+ env: {
112
+ ANTHROPIC_AUTH_TOKEN: input.token.trim() || "REPLACE_WITH_FULL_TOKEN",
113
+ ANTHROPIC_BASE_URL: input.baseUrl.trim(),
114
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: input.model.trim(),
115
+ ANTHROPIC_DEFAULT_OPUS_MODEL: input.model.trim(),
116
+ ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: input.model.trim(),
117
+ ANTHROPIC_DEFAULT_SONNET_MODEL: input.model.trim(),
118
+ ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: input.model.trim(),
119
+ ANTHROPIC_MODEL: input.model.trim()
120
+ }
121
+ });
122
+ await writeMeta(profileDir, { version: 1, type: "api", createdAt: new Date().toISOString() });
123
+ return summarizeProfile(input.name, profileDir);
124
+ }
125
+ export async function createLoginProfile(input, context = {}) {
126
+ const profileDir = await assertNewProfile(input.name, context);
127
+ await mkdir(profileDir, { recursive: true });
128
+ await writeSettings(profileDir, { theme: "dark" });
129
+ await writeMeta(profileDir, { version: 1, type: "login", createdAt: new Date().toISOString() });
130
+ return summarizeProfile(input.name, profileDir);
131
+ }
132
+ export async function removeProfile(name, context = {}) {
133
+ assertProfileName(name);
134
+ const profileDir = getProfileDir(name, context);
135
+ if (!(await exists(profileDir))) {
136
+ throw new CcpError(`Profile '${name}' does not exist: ${profileDir}`);
137
+ }
138
+ await removeProfileDir(profileDir);
139
+ return profileDir;
140
+ }
141
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/core/profiles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ClaudeSettings, ProfileMeta } from "./types.js";
2
+ export declare const SETTINGS_FILE = "settings.json";
3
+ export declare const META_FILE = ".ccp.json";
4
+ export declare function getSettingsPath(profileDir: string): string;
5
+ export declare function getMetaPath(profileDir: string): string;
6
+ export declare function readSettings(profileDir: string): Promise<ClaudeSettings | undefined>;
7
+ export declare function writeSettings(profileDir: string, settings: ClaudeSettings): Promise<void>;
8
+ export declare function readMeta(profileDir: string): Promise<ProfileMeta | undefined>;
9
+ export declare function writeMeta(profileDir: string, meta: ProfileMeta): Promise<void>;
10
+ export declare function removeProfileDir(profileDir: string): Promise<void>;
@@ -0,0 +1,44 @@
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { CcpError } from "./errors.js";
4
+ export const SETTINGS_FILE = "settings.json";
5
+ export const META_FILE = ".ccp.json";
6
+ async function readJsonFile(filePath) {
7
+ try {
8
+ const raw = await readFile(filePath, "utf8");
9
+ return JSON.parse(raw.replace(/^/, ""));
10
+ }
11
+ catch (error) {
12
+ if (error.code === "ENOENT") {
13
+ return undefined;
14
+ }
15
+ const reason = error instanceof Error ? ` ${error.message}` : "";
16
+ throw new CcpError(`Failed to read JSON file: ${filePath}.${reason}`);
17
+ }
18
+ }
19
+ async function writeJsonFile(filePath, value) {
20
+ await mkdir(path.dirname(filePath), { recursive: true });
21
+ await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
22
+ }
23
+ export function getSettingsPath(profileDir) {
24
+ return path.join(profileDir, SETTINGS_FILE);
25
+ }
26
+ export function getMetaPath(profileDir) {
27
+ return path.join(profileDir, META_FILE);
28
+ }
29
+ export async function readSettings(profileDir) {
30
+ return readJsonFile(getSettingsPath(profileDir));
31
+ }
32
+ export async function writeSettings(profileDir, settings) {
33
+ await writeJsonFile(getSettingsPath(profileDir), settings);
34
+ }
35
+ export async function readMeta(profileDir) {
36
+ return readJsonFile(getMetaPath(profileDir));
37
+ }
38
+ export async function writeMeta(profileDir, meta) {
39
+ await writeJsonFile(getMetaPath(profileDir), meta);
40
+ }
41
+ export async function removeProfileDir(profileDir) {
42
+ await rm(profileDir, { recursive: true, force: true });
43
+ }
44
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/core/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAC7C,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;AAErC,KAAK,UAAU,YAAY,CAAI,QAAgB;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAM,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,QAAQ,CAAC,6BAA6B,QAAQ,IAAI,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,KAAc;IAC3D,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB;IACnD,OAAO,YAAY,CAAiB,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB,EAAE,QAAwB;IAC9E,MAAM,aAAa,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,UAAkB;IAC/C,OAAO,YAAY,CAAc,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,UAAkB,EAAE,IAAiB;IACnE,MAAM,aAAa,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAkB;IACvD,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,34 @@
1
+ export type ProfileType = "api" | "login" | "ccr" | "unknown";
2
+ export interface ClaudeSettings {
3
+ theme?: string;
4
+ env?: Record<string, string>;
5
+ [key: string]: unknown;
6
+ }
7
+ export interface ProfileMeta {
8
+ version: number;
9
+ type: Exclude<ProfileType, "unknown">;
10
+ createdAt?: string;
11
+ endpoint?: string;
12
+ autoStart?: boolean;
13
+ ccrPreset?: string;
14
+ ccrRoute?: string;
15
+ }
16
+ export interface ProfileSummary {
17
+ name: string;
18
+ dir: string;
19
+ type: ProfileType;
20
+ baseUrl: string;
21
+ model: string;
22
+ tokenStatus: "set" | "missing";
23
+ settingsPath: string;
24
+ meta?: ProfileMeta;
25
+ }
26
+ export interface CreateApiProfileInput {
27
+ name: string;
28
+ baseUrl: string;
29
+ token: string;
30
+ model: string;
31
+ }
32
+ export interface CreateLoginProfileInput {
33
+ name: string;
34
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function getDefaultEditor(): string;
2
+ export declare function openEditor(filePath: string, editor?: string): Promise<number>;
@@ -0,0 +1,19 @@
1
+ import { spawn } from "node:child_process";
2
+ import { CcpError } from "../core/errors.js";
3
+ export function getDefaultEditor() {
4
+ if (process.env.EDITOR) {
5
+ return process.env.EDITOR;
6
+ }
7
+ if (process.platform === "win32") {
8
+ return "notepad";
9
+ }
10
+ return "vi";
11
+ }
12
+ export async function openEditor(filePath, editor = getDefaultEditor()) {
13
+ return new Promise((resolve, reject) => {
14
+ const child = spawn(editor, [filePath], { stdio: "inherit", shell: process.platform === "win32" });
15
+ child.on("error", (error) => reject(new CcpError(`Failed to open editor '${editor}': ${error.message}`)));
16
+ child.on("exit", (code) => resolve(code ?? 0));
17
+ });
18
+ }
19
+ //# sourceMappingURL=editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/platform/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,UAAU,gBAAgB;IAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;IAC5B,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAC5E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;QACnG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,0BAA0B,MAAM,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1G,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "multi-ccp",
3
+ "version": "0.1.0",
4
+ "description": "Cross-platform Claude Code profile manager for API, login, and router workflows.",
5
+ "type": "module",
6
+ "bin": {
7
+ "ccp": "dist/cli/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE",
13
+ "CHANGELOG.md"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc -p tsconfig.json",
17
+ "dev": "tsx src/cli/index.ts",
18
+ "test": "vitest run",
19
+ "typecheck": "tsc -p tsconfig.json --noEmit",
20
+ "prepublishOnly": "npm run build && npm test"
21
+ },
22
+ "keywords": [
23
+ "claude-code",
24
+ "claude",
25
+ "profiles",
26
+ "cli",
27
+ "anthropic"
28
+ ],
29
+ "author": "",
30
+ "license": "MIT",
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "dependencies": {
35
+ "@inquirer/prompts": "^7.10.1",
36
+ "commander": "^14.0.2",
37
+ "zod": "^4.1.13"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^24.10.1",
41
+ "tsx": "^4.21.0",
42
+ "typescript": "^5.9.3",
43
+ "vitest": "^4.0.15"
44
+ }
45
+ }