refactor: consolidate CCS files to unified ~/.ccs directory

Major reorganization to provide cleaner, more intuitive directory structure:

Directory changes:
- Move ~/.ccs.json → ~/.ccs/config.json
- Move ~/.claude/glm.settings.json → ~/.ccs/glm.settings.json
- Move ~/.claude/sonnet.settings.json → ~/.ccs/sonnet.settings.json
- Move ~/.local/share/ccs/* → ~/.ccs/
- Keep ~/.claude/settings.json unchanged (Claude CLI default)

Script improvements:
- Fix idempotency: check source != destination before cp
- Update all path references in ccs, install.sh, uninstall.sh
- Consolidate uninstall to single directory removal

Documentation updates:
- Update README badges: remove stars, modernize labels, add platform
- Update all path references in README.md and README.vi.md
- Update INSTALL-WORKFLOW.md diagram
- Update .ccs.example.json template

This provides a unified installation location that's easier to manage and understand.
This commit is contained in:
kaitranntt committed 2025-11-01 19:47:09 -04:00
1 parent 5227c0b28d
commit 40ff512c1b
8 files changed
+89 -96

No files matched your search

+2 -2
View File
@@ -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"
}
}
+1
View File
@@ -1,4 +1,5 @@
# User config files
# Note: config files now live in ~/.ccs/, not in project directory
.ccs.json
# macOS
+9 -9
View File
@@ -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<br/>INSTALL_DIR, SHARE_DIR, CLAUDE_DIR]
Start([Start Installation]) --> Init[Initialize Configuration<br/>INSTALL_DIR, CCS_DIR, CLAUDE_DIR]
Init --> DetectMethod{Detect Install Method<br/>ccs file exists?}
DetectMethod -->|Yes: SCRIPT_DIR/ccs| GitMethod[Method: git]
DetectMethod -->|No| StandaloneMethod[Method: standalone]
GitMethod --> CreateDirs[Create Directories<br/>mkdir INSTALL_DIR, SHARE_DIR]
GitMethod --> CreateDirs[Create Directories<br/>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<br/>chmod +x, ln -sf to INSTALL_DIR]
InstallExec -->|Standalone| DownloadCCS[Download ccs from GitHub<br/>to SHARE_DIR<br/>chmod +x, ln -sf to INSTALL_DIR]
InstallExec -->|Standalone| DownloadCCS[Download ccs from GitHub<br/>to CCS_DIR<br/>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<br/>+ 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<br/>with profile mappings]
CreateCCSConfig{~/.ccs/config.json exists?}
CreateCCSConfig -->|No| WriteCCSConfig[Create ~/.ccs/config.json<br/>with profile mappings]
CreateCCSConfig -->|Yes| SkipCCSConfig[Skip: Already exists]
WriteCCSConfig --> CheckCCSWrite{Write success?}
+22 -22
View File
@@ -1,8 +1,8 @@
# CCS - Claude Code Switch
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Bash](https://img.shields.io/badge/bash-3.2%2B-blue.svg)](https://www.gnu.org/software/bash/)
[![GitHub Stars](https://img.shields.io/github/stars/kaitranntt/ccs.svg)](https://github.com/kaitranntt/ccs/stargazers)
[![Language: Bash](https://img.shields.io/badge/Language-Bash-blue.svg)](https://www.gnu.org/software/bash/)
[![Platform: macOS | Linux](https://img.shields.io/badge/Platform-macOS%20%7C%20Linux-lightgrey.svg)]()
**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 <path> [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
+20 -20
View File
@@ -1,8 +1,8 @@
# CCS - Claude Code Switch
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Bash](https://img.shields.io/badge/bash-3.2%2B-blue.svg)](https://www.gnu.org/software/bash/)
[![GitHub Stars](https://img.shields.io/github/stars/kaitranntt/ccs.svg)](https://github.com/kaitranntt/ccs/stargazers)
[![Language: Bash](https://img.shields.io/badge/Language-Bash-blue.svg)](https://www.gnu.org/software/bash/)
[![Platform: macOS | Linux](https://img.shields.io/badge/Platform-macOS%20%7C%20Linux-lightgrey.svg)]()
**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
+2 -2
View File
@@ -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
+26 -28
View File
@@ -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
+7 -13
View File
@@ -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 ""