mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 18:15:23 +00:00
feat: add default profile, uninstall script, modernize docs
- Default profile: 'ccs' without args uses 'default' profile - Uninstall script: 'ccs-uninstall' removes installation cleanly - Install improvements: curl-compatible with uninstall bundling - Config cleanup: removed obsolete opus profile - README overhaul: professional badges, quick-start focus, one-liner install - Security: maintained injection prevention, input validation - Documentation: removed multiple accounts mention, updated URLs Closes #1
This commit is contained in:
1 parent
dd85d37af8
commit
35505530d0
5 files changed
+261
-103
No files matched your search
@@ -2,7 +2,6 @@
|
||||
"profiles": {
|
||||
"glm": "~/.claude/glm.settings.json",
|
||||
"sonnet": "~/.claude/sonnet.settings.json",
|
||||
"opus": "~/.claude/opus.settings.json",
|
||||
"default": "~/.claude/settings.json"
|
||||
}
|
||||
}
|
||||
@@ -1,80 +1,168 @@
|
||||
# CCS - Claude Code Switch
|
||||
|
||||
Ultra-simple wrapper for `claude --settings`. Switch between Claude profiles with friendly aliases.
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.gnu.org/software/bash/)
|
||||
[](https://github.com/kaitranntt/ccs/stargazers)
|
||||
|
||||
## Why?
|
||||
> Ultra-simple Claude CLI profile switcher. One command instead of long paths.
|
||||
|
||||
Claude CLI supports `--settings` flag but it's verbose:
|
||||
**Before**: `claude --settings ~/.claude/glm.settings.json --verbose`
|
||||
**After**: `ccs glm --verbose`
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Install** (one-liner):
|
||||
```bash
|
||||
claude --settings ~/.claude/glm.settings.json
|
||||
claude --settings ~/.claude/sonnet.settings.json --verbose
|
||||
curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/install.sh | bash
|
||||
```
|
||||
|
||||
CCS makes it friendly:
|
||||
|
||||
**Configure**:
|
||||
```bash
|
||||
ccs glm
|
||||
ccs sonnet --verbose
|
||||
# Edit with your profiles
|
||||
cat > ~/.ccs.json << 'EOF'
|
||||
{
|
||||
"profiles": {
|
||||
"glm": "~/.claude/glm.settings.json",
|
||||
"sonnet": "~/.claude/sonnet.settings.json",
|
||||
"default": "~/.claude/settings.json"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**Use**:
|
||||
```bash
|
||||
ccs # Use default profile
|
||||
ccs glm # Use GLM profile
|
||||
ccs sonnet # Use Sonnet profile
|
||||
```
|
||||
|
||||
## Why CCS?
|
||||
|
||||
Claude CLI's `--settings` flag is powerful but verbose. CCS gives you friendly aliases.
|
||||
|
||||
**Features**:
|
||||
- Single command profile switching
|
||||
- Pass-through all Claude CLI args
|
||||
- Zero configuration complexity
|
||||
- No proxies, no magic—just bash + jq
|
||||
|
||||
## Installation
|
||||
|
||||
### One-Liner (Recommended)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/install.sh | bash
|
||||
```
|
||||
|
||||
### Git Clone
|
||||
|
||||
```bash
|
||||
git clone https://github.com/kaitranntt/ccs.git
|
||||
cd ccs
|
||||
./install.sh
|
||||
```
|
||||
|
||||
Or as a one-liner:
|
||||
### Manual
|
||||
|
||||
```bash
|
||||
git clone https://github.com/kaitranntt/ccs.git && cd ccs && ./install.sh
|
||||
# Download script
|
||||
curl -fsSL https://raw.githubusercontent.com/kaitranntt/ccs/main/ccs -o ~/.local/bin/ccs
|
||||
chmod +x ~/.local/bin/ccs
|
||||
|
||||
# Ensure ~/.local/bin in PATH
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy example config:
|
||||
|
||||
```bash
|
||||
cp .ccs.example.json ~/.ccs.json
|
||||
```
|
||||
|
||||
Edit `~/.ccs.json` with your profiles:
|
||||
Create `~/.ccs.json` with profile mappings:
|
||||
|
||||
```json
|
||||
{
|
||||
"profiles": {
|
||||
"glm": "~/.claude/glm.settings.json",
|
||||
"sonnet": "~/.claude/sonnet.settings.json"
|
||||
"sonnet": "~/.claude/sonnet.settings.json",
|
||||
"default": "~/.claude/settings.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each profile points to a Claude settings JSON file. Create settings files per [Claude CLI docs](https://docs.claude.com/en/docs/claude-code/installation).
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic
|
||||
|
||||
```bash
|
||||
ccs glm # Use GLM profile
|
||||
ccs sonnet # Use Sonnet profile
|
||||
ccs # Use default profile (no args)
|
||||
ccs glm # Use GLM profile
|
||||
ccs sonnet # Use Sonnet profile
|
||||
```
|
||||
|
||||
### Pass Arguments Transparently
|
||||
### With Arguments
|
||||
|
||||
All arguments after the profile name are passed directly to Claude:
|
||||
All args after profile name pass directly to Claude CLI:
|
||||
|
||||
```bash
|
||||
ccs glm --verbose
|
||||
ccs sonnet /plan "add feature"
|
||||
ccs opus --model claude-opus-4
|
||||
ccs default --model claude-sonnet-4
|
||||
```
|
||||
|
||||
### Custom Config Location
|
||||
|
||||
```bash
|
||||
export CCS_CONFIG=~/my-custom-ccs.json
|
||||
ccs glm
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Claude Subscription + GLM Coding Plan
|
||||
|
||||
Switch between Claude sub and GLM plan:
|
||||
|
||||
```json
|
||||
{
|
||||
"profiles": {
|
||||
"claude": "~/.claude/claude-sub.settings.json",
|
||||
"glm": "~/.claude/glm.settings.json",
|
||||
"default": "~/.claude/settings.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
ccs claude # Use Claude subscription
|
||||
ccs glm # Use GLM coding plan
|
||||
```
|
||||
|
||||
### Different Models
|
||||
|
||||
```json
|
||||
{
|
||||
"profiles": {
|
||||
"sonnet": "~/.claude/sonnet.settings.json",
|
||||
"haiku": "~/.claude/haiku.settings.json",
|
||||
"default": "~/.claude/settings.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Reads profile name (defaults to "default" if omitted)
|
||||
2. Looks up settings file path in `~/.ccs.json`
|
||||
3. Executes `claude --settings <path> [remaining-args]`
|
||||
|
||||
No magic. No file modification. Pure delegation.
|
||||
|
||||
## Requirements
|
||||
|
||||
- `bash` (3.2+ compatible)
|
||||
- `bash` 3.2+
|
||||
- `jq` (JSON processor)
|
||||
- [Claude CLI](https://docs.claude.com/en/docs/claude-code/installation) installed
|
||||
- [Claude CLI](https://docs.claude.com/en/docs/claude-code/installation)
|
||||
|
||||
### Installing jq
|
||||
|
||||
@@ -87,95 +175,67 @@ sudo apt install jq
|
||||
|
||||
# Fedora
|
||||
sudo dnf install jq
|
||||
|
||||
# Arch
|
||||
sudo pacman -S jq
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Reads profile name from first argument
|
||||
2. Looks up settings file path in `~/.ccs.json`
|
||||
3. Executes `claude --settings <path> [remaining-args]`
|
||||
|
||||
That's it. No magic, no proxies, no file modification.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Profile not found
|
||||
|
||||
```bash
|
||||
```
|
||||
Error: Profile 'foo' not found in ~/.ccs.json
|
||||
```
|
||||
|
||||
**Solution**: Add the profile to your `~/.ccs.json` config file.
|
||||
**Fix**: Add profile to `~/.ccs.json`:
|
||||
```json
|
||||
{
|
||||
"profiles": {
|
||||
"foo": "~/.claude/foo.settings.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Settings file missing
|
||||
|
||||
```bash
|
||||
```
|
||||
Error: Settings file not found: ~/.claude/foo.settings.json
|
||||
```
|
||||
|
||||
**Solution**: Create the settings file or update the path in your config.
|
||||
**Fix**: Create settings file or fix path in config.
|
||||
|
||||
### jq not installed
|
||||
|
||||
```bash
|
||||
```
|
||||
Error: jq is required but not installed
|
||||
```
|
||||
|
||||
**Solution**: Install jq using your package manager (see Requirements section).
|
||||
**Fix**: Install jq (see Requirements).
|
||||
|
||||
### PATH not set
|
||||
|
||||
```bash
|
||||
```
|
||||
⚠️ Warning: ~/.local/bin is not in PATH
|
||||
```
|
||||
|
||||
**Solution**: Add to your shell profile (`~/.bashrc` or `~/.zshrc`):
|
||||
|
||||
**Fix**: Add to `~/.bashrc` or `~/.zshrc`:
|
||||
```bash
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
```
|
||||
Then `source ~/.bashrc` or restart shell.
|
||||
|
||||
Then restart your shell or run `source ~/.bashrc` (or `~/.zshrc`).
|
||||
### Default profile missing
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Claude Subscription + GLM Coding Plan
|
||||
|
||||
If you have both Claude subscription and GLM coding plan:
|
||||
```
|
||||
Error: Profile 'default' not found in ~/.ccs.json
|
||||
```
|
||||
|
||||
**Fix**: Add "default" profile or always specify profile name:
|
||||
```json
|
||||
{
|
||||
"profiles": {
|
||||
"claude": "~/.claude/sonnet.settings.json",
|
||||
"glm": "~/.claude/glm.settings.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
ccs claude # Use Claude subscription
|
||||
ccs glm # Use GLM coding plan
|
||||
```
|
||||
|
||||
### Multiple Anthropic Accounts
|
||||
|
||||
```json
|
||||
{
|
||||
"profiles": {
|
||||
"work": "~/.claude/work.settings.json",
|
||||
"personal": "~/.claude/personal.settings.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Different Models
|
||||
|
||||
```json
|
||||
{
|
||||
"profiles": {
|
||||
"sonnet": "~/.claude/sonnet.settings.json",
|
||||
"opus": "~/.claude/opus.settings.json",
|
||||
"haiku": "~/.claude/haiku.settings.json"
|
||||
"default": "~/.claude/settings.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -183,22 +243,40 @@ ccs glm # Use GLM coding plan
|
||||
## Uninstallation
|
||||
|
||||
```bash
|
||||
rm ~/.local/bin/ccs
|
||||
rm ~/.ccs.json
|
||||
ccs-uninstall
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Or manual:
|
||||
```bash
|
||||
rm ~/.local/bin/ccs
|
||||
rm ~/.local/bin/ccs-uninstall
|
||||
rm ~/.ccs.json # If you want to remove config
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
PRs welcome! Keep it simple (KISS principle).
|
||||
|
||||
**Guidelines**:
|
||||
- Maintain bash 3.2+ compatibility
|
||||
- No dependencies beyond jq
|
||||
- Test on macOS and Linux
|
||||
- Follow existing code style
|
||||
|
||||
## Philosophy
|
||||
|
||||
**YAGNI**: We don't add features "just in case"
|
||||
**KISS**: Simple bash script, no complexity
|
||||
**DRY**: One source of truth (config file)
|
||||
- **YAGNI**: No features "just in case"
|
||||
- **KISS**: Simple bash, no complexity
|
||||
- **DRY**: One source of truth (config)
|
||||
|
||||
This tool does ONE thing well: map profile names to settings files. Nothing more.
|
||||
This tool does ONE thing well: map profile names to settings files.
|
||||
|
||||
## License
|
||||
|
||||
MIT © [Kai Tran](https://github.com/kaitranntt)
|
||||
|
||||
## Links
|
||||
|
||||
- [Claude CLI Docs](https://docs.claude.com/en/docs/claude-code/installation)
|
||||
- [Report Issues](https://github.com/kaitranntt/ccs/issues)
|
||||
- [Changelog](https://github.com/kaitranntt/ccs/releases)
|
||||
@@ -2,16 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs.json}"
|
||||
PROFILE="${1:-}"
|
||||
|
||||
# Validate usage
|
||||
if [[ -z "$PROFILE" ]]; then
|
||||
echo "Usage: ccs <profile> [claude-args...]"
|
||||
echo "Example: ccs glm --verbose"
|
||||
echo ""
|
||||
echo "Available profiles are defined in: $CONFIG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
PROFILE="${1:-default}"
|
||||
|
||||
# Check config exists
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
|
||||
+49
-4
@@ -3,12 +3,21 @@ set -euo pipefail
|
||||
|
||||
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SHARE_DIR="$HOME/.local/share/ccs"
|
||||
|
||||
# Determine if running from git clone or curl
|
||||
if [[ -d "$SCRIPT_DIR/.git" ]]; then
|
||||
INSTALL_METHOD="git"
|
||||
else
|
||||
INSTALL_METHOD="standalone"
|
||||
fi
|
||||
|
||||
echo "Installing ccs to $INSTALL_DIR..."
|
||||
echo ""
|
||||
|
||||
# Create install dir if needed
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
mkdir -p "$SHARE_DIR"
|
||||
|
||||
# Make executable first
|
||||
chmod +x "$SCRIPT_DIR/ccs"
|
||||
@@ -23,6 +32,23 @@ if [[ ! -L "$INSTALL_DIR/ccs" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install uninstall script
|
||||
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"
|
||||
elif [[ "$INSTALL_METHOD" == "standalone" ]]; then
|
||||
# Fetch uninstall script for curl installs
|
||||
echo "Fetching uninstall script..."
|
||||
if command -v curl &> /dev/null; then
|
||||
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"
|
||||
else
|
||||
echo "⚠️ Warning: curl not found, skipping uninstall script"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if in PATH
|
||||
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
||||
echo "⚠️ Warning: $INSTALL_DIR is not in PATH"
|
||||
@@ -35,10 +61,29 @@ fi
|
||||
echo "✅ Installation complete!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Copy example config: cp $SCRIPT_DIR/.ccs.example.json ~/.ccs.json"
|
||||
echo "2. Edit ~/.ccs.json with your profile mappings"
|
||||
echo "3. Run: ccs <profile>"
|
||||
if [[ ! -f "$HOME/.ccs.json" ]]; then
|
||||
if [[ -f "$SCRIPT_DIR/.ccs.example.json" ]]; then
|
||||
echo "1. Copy example config: cp $SCRIPT_DIR/.ccs.example.json ~/.ccs.json"
|
||||
else
|
||||
echo "1. Create config at ~/.ccs.json:"
|
||||
echo " {"
|
||||
echo " \"profiles\": {"
|
||||
echo " \"glm\": \"~/.claude/glm.settings.json\","
|
||||
echo " \"sonnet\": \"~/.claude/sonnet.settings.json\","
|
||||
echo " \"default\": \"~/.claude/settings.json\""
|
||||
echo " }"
|
||||
echo " }"
|
||||
fi
|
||||
echo "2. Edit ~/.ccs.json with your profile mappings"
|
||||
echo "3. Run: ccs [profile]"
|
||||
else
|
||||
echo "1. Config already exists at ~/.ccs.json"
|
||||
echo "2. Run: ccs [profile]"
|
||||
fi
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " ccs glm"
|
||||
echo " ccs # Uses default profile"
|
||||
echo " ccs glm # Uses glm profile"
|
||||
echo " ccs sonnet --verbose"
|
||||
echo ""
|
||||
echo "To uninstall: ccs-uninstall"
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Uninstalling ccs..."
|
||||
echo ""
|
||||
|
||||
# Remove symlink
|
||||
if [[ -L "$HOME/.local/bin/ccs" ]]; then
|
||||
rm "$HOME/.local/bin/ccs"
|
||||
echo "✅ Removed: $HOME/.local/bin/ccs"
|
||||
elif [[ -f "$HOME/.local/bin/ccs" ]]; then
|
||||
rm "$HOME/.local/bin/ccs"
|
||||
echo "✅ Removed: $HOME/.local/bin/ccs"
|
||||
else
|
||||
echo "ℹ️ No ccs binary found at $HOME/.local/bin/ccs"
|
||||
fi
|
||||
|
||||
# Remove uninstall symlink
|
||||
if [[ -L "$HOME/.local/bin/ccs-uninstall" ]]; then
|
||||
rm "$HOME/.local/bin/ccs-uninstall"
|
||||
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
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
rm "$HOME/.ccs.json"
|
||||
echo "✅ Removed: $HOME/.ccs.json"
|
||||
else
|
||||
echo "ℹ️ Kept: $HOME/.ccs.json"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ No config file found at $HOME/.ccs.json"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ Uninstall complete!"
|
||||
Reference in new issue
Block a user