diff --git a/.ccs.example.json b/.ccs.example.json
index 063c868c..7e7cbe30 100644
--- a/.ccs.example.json
+++ b/.ccs.example.json
@@ -1,7 +1,7 @@
{
"profiles": {
- "glm": "~/.claude/glm.settings.json",
- "sonnet": "~/.claude/sonnet.settings.json",
+ "glm": "~/.ccs/glm.settings.json",
+ "sonnet": "~/.ccs/sonnet.settings.json",
"default": "~/.claude/settings.json"
}
}
diff --git a/.gitignore b/.gitignore
index 0d54ab81..f68f2caa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
# User config files
+# Note: config files now live in ~/.ccs/, not in project directory
.ccs.json
# macOS
diff --git a/INSTALL-WORKFLOW.md b/INSTALL-WORKFLOW.md
index 87c6128a..6c525002 100644
--- a/INSTALL-WORKFLOW.md
+++ b/INSTALL-WORKFLOW.md
@@ -6,13 +6,13 @@ This diagram illustrates the installation flow for the `ccs` (claude-code-switch
```mermaid
flowchart TD
- Start([Start Installation]) --> Init[Initialize Configuration
INSTALL_DIR, SHARE_DIR, CLAUDE_DIR]
+ Start([Start Installation]) --> Init[Initialize Configuration
INSTALL_DIR, CCS_DIR, CLAUDE_DIR]
Init --> DetectMethod{Detect Install Method
ccs file exists?}
DetectMethod -->|Yes: SCRIPT_DIR/ccs| GitMethod[Method: git]
DetectMethod -->|No| StandaloneMethod[Method: standalone]
- GitMethod --> CreateDirs[Create Directories
mkdir INSTALL_DIR, SHARE_DIR]
+ GitMethod --> CreateDirs[Create Directories
mkdir INSTALL_DIR, CCS_DIR]
StandaloneMethod --> CheckCurl{curl available?}
CheckCurl -->|No| ErrorCurl[❌ Error: curl required]
CheckCurl -->|Yes| CreateDirs
@@ -21,7 +21,7 @@ flowchart TD
CreateDirs --> InstallExec{Install Method?}
InstallExec -->|Git| InstallGit[Use local ccs from SCRIPT_DIR
chmod +x, ln -sf to INSTALL_DIR]
- InstallExec -->|Standalone| DownloadCCS[Download ccs from GitHub
to SHARE_DIR
chmod +x, ln -sf to INSTALL_DIR]
+ InstallExec -->|Standalone| DownloadCCS[Download ccs from GitHub
to CCS_DIR
chmod +x, ln -sf to INSTALL_DIR]
InstallGit --> CheckSymlink{Symlink Created?}
DownloadCCS --> CheckDownload{Download success?}
@@ -78,9 +78,9 @@ flowchart TD
CheckJQTemplate -->|Yes| MergeTemplate[Merge with current]
CheckJQTemplate -->|No| BasicTemplate[Use basic template]
MergeTemplate --> CheckMerge{Merge success?}
- CheckMerge -->|Yes| AtomicMVGLM[atomic_mv to glm.settings.json]
+ CheckMerge -->|Yes| AtomicMVGLM[atomic_mv to ~/.ccs/glm.settings.json]
CheckMerge -->|No| BasicTemplate
- BasicTemplate --> WriteGLM[Write glm.settings.json]
+ BasicTemplate --> WriteGLM[Write ~/.ccs/glm.settings.json]
AtomicMVGLM --> CheckAtomicGLM{Move success?}
CheckAtomicGLM -->|No| ErrorAtomicGLM[❌ Error: Permission denied]
@@ -97,12 +97,12 @@ flowchart TD
CreateSonnet -->|Yes| CopySonnetConfig[Copy current config]
CreateSonnet -->|No| CreateSonnetTemplate[Create Sonnet template
+ remove custom settings with jq]
- CopySonnetConfig --> WriteSonnet[Write sonnet.settings.json]
+ CopySonnetConfig --> WriteSonnet[Write ~/.ccs/sonnet.settings.json]
CreateSonnetTemplate --> CheckJQSonnet{jq available?}
CheckJQSonnet -->|Yes| RemoveCustom[Remove ANTHROPIC_BASE_URL, etc.]
CheckJQSonnet -->|No| BasicSonnet[Use basic template]
RemoveCustom --> CheckRemove{Remove success?}
- CheckRemove -->|Yes| AtomicMVSonnet[atomic_mv to sonnet.settings.json]
+ CheckRemove -->|Yes| AtomicMVSonnet[atomic_mv to ~/.ccs/sonnet.settings.json]
CheckRemove -->|No| BasicSonnet
BasicSonnet --> WriteSonnet
@@ -114,8 +114,8 @@ flowchart TD
WriteSonnet --> CreateCCSConfig
- CreateCCSConfig{~/.ccs.json exists?}
- CreateCCSConfig -->|No| WriteCCSConfig[Create ~/.ccs.json
with profile mappings]
+ CreateCCSConfig{~/.ccs/config.json exists?}
+ CreateCCSConfig -->|No| WriteCCSConfig[Create ~/.ccs/config.json
with profile mappings]
CreateCCSConfig -->|Yes| SkipCCSConfig[Skip: Already exists]
WriteCCSConfig --> CheckCCSWrite{Write success?}
diff --git a/README.md b/README.md
index 2bb724d7..9f1f4ad0 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# CCS - Claude Code Switch
[](https://opensource.org/licenses/MIT)
-[](https://www.gnu.org/software/bash/)
-[](https://github.com/kaitranntt/ccs/stargazers)
+[](https://www.gnu.org/software/bash/)
+[]()
**Languages**: [English](README.md) | [Tiếng Việt](README.vi.md)
@@ -34,11 +34,11 @@ curl -fsSL ccs.kaitran.ca/install | bash
**Configure**:
```bash
# Edit with your profiles
-cat > ~/.ccs.json << 'EOF'
+cat > ~/.ccs/config.json << 'EOF'
{
"profiles": {
- "glm": "~/.claude/glm.settings.json",
- "son": "~/.claude/sonnet.settings.json",
+ "glm": "~/.ccs/glm.settings.json",
+ "son": "~/.ccs/sonnet.settings.json",
"default": "~/.claude/settings.json"
}
}
@@ -156,13 +156,13 @@ curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/install.sh | ba
## Configuration
-The installer auto-creates `~/.ccs.json` and profile templates during installation. If you need to customize:
+The installer auto-creates `~/.ccs/config.json` and profile templates during installation. If you need to customize:
```json
{
"profiles": {
- "glm": "~/.claude/glm.settings.json",
- "sonnet": "~/.claude/sonnet.settings.json",
+ "glm": "~/.ccs/glm.settings.json",
+ "sonnet": "~/.ccs/sonnet.settings.json",
"default": "~/.claude/settings.json"
}
}
@@ -217,7 +217,7 @@ ccs default --model claude-sonnet-4
### Custom Config Location
```bash
-export CCS_CONFIG=~/my-custom-ccs.json
+export CCS_CONFIG=~/my-custom-config.json
ccs glm
```
@@ -275,8 +275,8 @@ ccs son
```json
{
"profiles": {
- "glm": "~/.claude/glm.settings.json",
- "sonnet": "~/.claude/sonnet.settings.json",
+ "glm": "~/.ccs/glm.settings.json",
+ "sonnet": "~/.ccs/sonnet.settings.json",
"default": "~/.claude/settings.json"
}
}
@@ -286,9 +286,9 @@ ccs son
```json
{
"profiles": {
- "sonnet": "~/.claude/sonnet.settings.json",
- "glm": "~/.claude/glm.settings.json",
- "haiku": "~/.claude/haiku.settings.json",
+ "sonnet": "~/.ccs/sonnet.settings.json",
+ "glm": "~/.ccs/glm.settings.json",
+ "haiku": "~/.ccs/haiku.settings.json",
"default": "~/.claude/settings.json"
}
}
@@ -297,7 +297,7 @@ ccs son
## How It Works
1. Reads profile name (defaults to "default" if omitted)
-2. Looks up settings file path in `~/.ccs.json`
+2. Looks up settings file path in `~/.ccs/config.json`
3. Executes `claude --settings [remaining-args]`
No magic. No file modification. Pure delegation.
@@ -352,14 +352,14 @@ If installing from a git worktree or submodule, older versions may fail to detec
#### Profile not found
```
-Error: Profile 'foo' not found in ~/.ccs.json
+Error: Profile 'foo' not found in ~/.ccs/config.json
```
-**Fix**: Add profile to `~/.ccs.json`:
+**Fix**: Add profile to `~/.ccs/config.json`:
```json
{
"profiles": {
- "foo": "~/.claude/foo.settings.json"
+ "foo": "~/.ccs/foo.settings.json"
}
}
```
@@ -367,7 +367,7 @@ Error: Profile 'foo' not found in ~/.ccs.json
#### Settings file missing
```
-Error: Settings file not found: ~/.claude/foo.settings.json
+Error: Settings file not found: ~/.ccs/foo.settings.json
```
**Fix**: Create settings file or fix path in config.
@@ -399,7 +399,7 @@ Then `source ~/.bashrc` or restart shell.
#### Default profile missing
```
-Error: Profile 'default' not found in ~/.ccs.json
+Error: Profile 'default' not found in ~/.ccs/config.json
```
**Fix**: Add "default" profile or always specify profile name:
@@ -417,7 +417,7 @@ Error: Profile 'default' not found in ~/.ccs.json
**Not a problem**: The installer preserves existing API keys when upgrading. If you're using GLM, your API key is automatically preserved and the profile is enhanced with new default model variables.
-**Verification**: Check `~/.claude/glm.settings.json` - your `ANTHROPIC_AUTH_TOKEN` should still be present.
+**Verification**: Check `~/.ccs/glm.settings.json` - your `ANTHROPIC_AUTH_TOKEN` should still be present.
## Uninstallation
@@ -439,7 +439,7 @@ curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/uninstall.sh |
```bash
rm ~/.local/bin/ccs
rm ~/.local/bin/ccs-uninstall
-rm ~/.ccs.json # If you want to remove config
+rm -rf ~/.ccs # If you want to remove all CCS files
```
## Contributing
diff --git a/README.vi.md b/README.vi.md
index 0381ada9..0439cd4c 100644
--- a/README.vi.md
+++ b/README.vi.md
@@ -1,8 +1,8 @@
# CCS - Claude Code Switch
[](https://opensource.org/licenses/MIT)
-[](https://www.gnu.org/software/bash/)
-[](https://github.com/kaitranntt/ccs/stargazers)
+[](https://www.gnu.org/software/bash/)
+[]()
**Ngôn ngữ**: [English](README.md) | [Tiếng Việt](README.vi.md)
@@ -34,11 +34,11 @@ curl -fsSL ccs.kaitran.ca/install | bash
**Cấu hình**:
```bash
# Sửa theo profile của bạn
-cat > ~/.ccs.json << 'EOF'
+cat > ~/.ccs/config.json << 'EOF'
{
"profiles": {
- "glm": "~/.claude/glm.settings.json",
- "son": "~/.claude/sonnet.settings.json",
+ "glm": "~/.ccs/glm.settings.json",
+ "son": "~/.ccs/sonnet.settings.json",
"default": "~/.claude/settings.json"
}
}
@@ -147,13 +147,13 @@ curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/install.sh | ba
## Cấu Hình
-Installer tự động tạo `~/.ccs.json` và profile templates khi cài đặt. Nếu cần tùy chỉnh:
+Installer tự động tạo `~/.ccs/config.json` và profile templates khi cài đặt. Nếu cần tùy chỉnh:
```json
{
"profiles": {
- "glm": "~/.claude/glm.settings.json",
- "son": "~/.claude/sonnet.settings.json",
+ "glm": "~/.ccs/glm.settings.json",
+ "son": "~/.ccs/sonnet.settings.json",
"default": "~/.claude/settings.json"
}
}
@@ -264,9 +264,9 @@ ccs son
```json
{
"profiles": {
- "glm1": "~/.claude/glm-account1.settings.json",
- "glm2": "~/.claude/glm-account2.settings.json",
- "son": "~/.claude/sonnet.settings.json"
+ "glm1": "~/.ccs/glm-account1.settings.json",
+ "glm2": "~/.ccs/glm-account2.settings.json",
+ "son": "~/.ccs/sonnet.settings.json"
}
}
```
@@ -275,9 +275,9 @@ ccs son
```json
{
"profiles": {
- "work": "~/.claude/work.settings.json",
- "personal": "~/.claude/personal.settings.json",
- "experiments": "~/.claude/experiments.settings.json"
+ "work": "~/.ccs/work.settings.json",
+ "personal": "~/.ccs/personal.settings.json",
+ "experiments": "~/.ccs/experiments.settings.json"
}
}
```
@@ -338,14 +338,14 @@ Nếu cài từ git worktree hoặc submodule, phiên bản cũ có thể không
#### Profile không tìm thấy
```
-Error: Profile 'foo' not found in ~/.ccs.json
+Error: Profile 'foo' not found in ~/.ccs/config.json
```
**Fix**: Thêm profile vào config:
```json
{
"profiles": {
- "foo": "~/.claude/foo.settings.json"
+ "foo": "~/.ccs/foo.settings.json"
}
}
```
@@ -353,7 +353,7 @@ Error: Profile 'foo' not found in ~/.ccs.json
#### File settings thiếu
```
-Error: Settings file not found: ~/.claude/foo.settings.json
+Error: Settings file not found: ~/.ccs/foo.settings.json
```
**Fix**: Tạo file settings hoặc sửa path trong config.
@@ -385,7 +385,7 @@ Sau đó `source ~/.bashrc` hoặc khởi động lại shell.
#### Profile mặc định thiếu
```
-Error: Profile 'default' not found in ~/.ccs.json
+Error: Profile 'default' not found in ~/.ccs/config.json
```
**Fix**: Thêm profile default:
@@ -403,7 +403,7 @@ Error: Profile 'default' not found in ~/.ccs.json
**Không phải vấn đề**: Installer giữ nguyên API keys hiện tại khi nâng cấp. Nếu bạn đang dùng GLM, API key của bạn được tự động giữ lại và profile được nâng cấp với các biến default model mới.
-**Xác minh**: Kiểm tra `~/.claude/glm.settings.json` - `ANTHROPIC_AUTH_TOKEN` của bạn vẫn còn đó.
+**Xác minh**: Kiểm tra `~/.ccs/glm.settings.json` - `ANTHROPIC_AUTH_TOKEN` của bạn vẫn còn đó.
## Gỡ Cài Đặt
@@ -425,7 +425,7 @@ curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/uninstall.sh |
```bash
rm ~/.local/bin/ccs
rm ~/.local/bin/ccs-uninstall
-rm ~/.ccs.json # Nếu muốn xóa config
+rm -rf ~/.ccs # Nếu muốn xóa tất cả files của CCS
```
## Bảo Mật
diff --git a/ccs b/ccs
index a653274b..7bf5bd6b 100755
--- a/ccs
+++ b/ccs
@@ -1,14 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
-CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs.json}"
+CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}"
PROFILE="${1:-default}"
# Check config exists
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "Error: Config file not found: $CONFIG_FILE"
echo ""
- echo "Create ~/.ccs.json with your profile mappings."
+ echo "Create ~/.ccs/config.json with your profile mappings."
echo "See .ccs.example.json for template."
exit 1
fi
diff --git a/install.sh b/install.sh
index c4a1e536..39c3d607 100755
--- a/install.sh
+++ b/install.sh
@@ -7,7 +7,7 @@ set -euo pipefail
# --- Configuration ---
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}"
-SHARE_DIR="$HOME/.local/share/ccs"
+CCS_DIR="$HOME/.ccs"
CLAUDE_DIR="$HOME/.claude"
GLM_MODEL="glm-4.6"
@@ -82,7 +82,7 @@ atomic_mv() {
create_glm_profile() {
local current_settings="$CLAUDE_DIR/settings.json"
- local glm_settings="$CLAUDE_DIR/glm.settings.json"
+ local glm_settings="$CCS_DIR/glm.settings.json"
local provider="$1"
if [[ "$provider" == "glm" ]]; then
@@ -131,7 +131,7 @@ create_glm_profile() {
create_sonnet_profile() {
local current_settings="$CLAUDE_DIR/settings.json"
- local sonnet_settings="$CLAUDE_DIR/sonnet.settings.json"
+ local sonnet_settings="$CCS_DIR/sonnet.settings.json"
local provider="$1"
if [[ "$provider" == "claude" ]]; then
@@ -171,7 +171,7 @@ echo "Installing ccs to $INSTALL_DIR..."
echo ""
# Create directories
-mkdir -p "$INSTALL_DIR" "$SHARE_DIR"
+mkdir -p "$INSTALL_DIR" "$CCS_DIR"
# Install main executable
if [[ "$INSTALL_METHOD" == "standalone" ]]; then
@@ -182,9 +182,9 @@ if [[ "$INSTALL_METHOD" == "standalone" ]]; then
fi
echo "Fetching ccs executable..."
- if curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/ccs -o "$SHARE_DIR/ccs"; then
- chmod +x "$SHARE_DIR/ccs"
- ln -sf "$SHARE_DIR/ccs" "$INSTALL_DIR/ccs"
+ if curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/ccs -o "$CCS_DIR/ccs"; then
+ chmod +x "$CCS_DIR/ccs"
+ ln -sf "$CCS_DIR/ccs" "$INSTALL_DIR/ccs"
else
echo "❌ Error: Failed to download ccs from GitHub"
exit 1
@@ -201,16 +201,19 @@ if [[ ! -L "$INSTALL_DIR/ccs" ]]; then
exit 1
fi
-# Install uninstall script
+# Install uninstall script (with idempotency check)
if [[ -f "$SCRIPT_DIR/uninstall.sh" ]]; then
- cp "$SCRIPT_DIR/uninstall.sh" "$SHARE_DIR/uninstall.sh"
- chmod +x "$SHARE_DIR/uninstall.sh"
- ln -sf "$SHARE_DIR/uninstall.sh" "$INSTALL_DIR/ccs-uninstall"
+ # Only copy if source and destination are different
+ if [[ "$SCRIPT_DIR/uninstall.sh" != "$CCS_DIR/uninstall.sh" ]]; then
+ cp "$SCRIPT_DIR/uninstall.sh" "$CCS_DIR/uninstall.sh"
+ fi
+ chmod +x "$CCS_DIR/uninstall.sh"
+ ln -sf "$CCS_DIR/uninstall.sh" "$INSTALL_DIR/ccs-uninstall"
elif [[ "$INSTALL_METHOD" == "standalone" ]] && command -v curl &> /dev/null; then
echo "Fetching uninstall script..."
- curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/uninstall.sh -o "$SHARE_DIR/uninstall.sh"
- chmod +x "$SHARE_DIR/uninstall.sh"
- ln -sf "$SHARE_DIR/uninstall.sh" "$INSTALL_DIR/ccs-uninstall"
+ curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/uninstall.sh -o "$CCS_DIR/uninstall.sh"
+ chmod +x "$CCS_DIR/uninstall.sh"
+ ln -sf "$CCS_DIR/uninstall.sh" "$INSTALL_DIR/ccs-uninstall"
fi
# Check PATH
@@ -228,8 +231,8 @@ echo ""
# --- Profile Setup ---
CURRENT_PROVIDER=$(detect_current_provider)
-GLM_SETTINGS="$CLAUDE_DIR/glm.settings.json"
-SONNET_SETTINGS="$CLAUDE_DIR/sonnet.settings.json"
+GLM_SETTINGS="$CCS_DIR/glm.settings.json"
+SONNET_SETTINGS="$CCS_DIR/sonnet.settings.json"
[[ "$CURRENT_PROVIDER" != "unknown" ]] && echo "🔍 Detected current provider: $CURRENT_PROVIDER" && echo ""
@@ -243,24 +246,19 @@ if [[ ! -f "$GLM_SETTINGS" ]] || [[ ! -f "$SONNET_SETTINGS" ]]; then
fi
# Create ccs config
-if [[ ! -f "$HOME/.ccs.json" ]]; then
- echo "Creating ~/.ccs.json config..."
- cat > "$HOME/.ccs.json.tmp" << 'EOF'
+if [[ ! -f "$CCS_DIR/config.json" ]]; then
+ echo "Creating ~/.ccs/config.json..."
+ cat > "$CCS_DIR/config.json.tmp" << 'EOF'
{
"profiles": {
- "glm": "~/.claude/glm.settings.json",
- "son": "~/.claude/sonnet.settings.json",
+ "glm": "~/.ccs/glm.settings.json",
+ "son": "~/.ccs/sonnet.settings.json",
"default": "~/.claude/settings.json"
}
}
EOF
- if [[ ! -f "$HOME/.ccs.json" ]]; then
- atomic_mv "$HOME/.ccs.json.tmp" "$HOME/.ccs.json"
- echo " ✓ Created: ~/.ccs.json"
- else
- rm -f "$HOME/.ccs.json.tmp"
- echo " ℹ️ Config already exists"
- fi
+ atomic_mv "$CCS_DIR/config.json.tmp" "$CCS_DIR/config.json"
+ echo " ✓ Created: ~/.ccs/config.json"
echo ""
fi
diff --git a/uninstall.sh b/uninstall.sh
index 0d614fba..acd6e29b 100755
--- a/uninstall.sh
+++ b/uninstall.sh
@@ -21,24 +21,18 @@ if [[ -L "$HOME/.local/bin/ccs-uninstall" ]]; then
echo "✅ Removed: $HOME/.local/bin/ccs-uninstall"
fi
-# Remove share directory
-if [[ -d "$HOME/.local/share/ccs" ]]; then
- rm -rf "$HOME/.local/share/ccs"
- echo "✅ Removed: $HOME/.local/share/ccs"
-fi
-
-# Ask about config
-if [[ -f "$HOME/.ccs.json" ]]; then
- read -p "Remove config file ~/.ccs.json? (y/N) " -n 1 -r
+# Ask about ~/.ccs directory
+if [[ -d "$HOME/.ccs" ]]; then
+ read -p "Remove CCS directory ~/.ccs? This includes config and profiles. (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
- rm "$HOME/.ccs.json"
- echo "✅ Removed: $HOME/.ccs.json"
+ rm -rf "$HOME/.ccs"
+ echo "✅ Removed: $HOME/.ccs"
else
- echo "ℹ️ Kept: $HOME/.ccs.json"
+ echo "ℹ️ Kept: $HOME/.ccs"
fi
else
- echo "ℹ️ No config file found at $HOME/.ccs.json"
+ echo "ℹ️ No CCS directory found at $HOME/.ccs"
fi
echo ""