feat: initial commit - ultra-simple claude code switcher

- Core ccs bash script with profile switching
- Installation script with PATH verification
- Example config template
- Comprehensive README with troubleshooting
- Security: input validation, injection prevention
- MIT license
This commit is contained in:
kaitranntt
2025-11-01 17:01:42 -04:00
commit c8f85a1220
6 changed files with 370 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
{
"profiles": {
"glm": "~/.claude/glm.settings.json",
"sonnet": "~/.claude/sonnet.settings.json",
"opus": "~/.claude/opus.settings.json",
"default": "~/.claude/settings.json"
}
}
+12
View File
@@ -0,0 +1,12 @@
# User config files
.ccs.json
# macOS
.DS_Store
# Editor
.vscode/
.idea/
*.swp
*.swo
*~
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 CCS Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+204
View File
@@ -0,0 +1,204 @@
# CCS - Claude Code Switch
Ultra-simple wrapper for `claude --settings`. Switch between Claude profiles with friendly aliases.
## Why?
Claude CLI supports `--settings` flag but it's verbose:
```bash
claude --settings ~/.claude/glm.settings.json
claude --settings ~/.claude/sonnet.settings.json --verbose
```
CCS makes it friendly:
```bash
ccs glm
ccs sonnet --verbose
```
## Installation
```bash
git clone https://github.com/yourusername/ccs.git
cd ccs
./install.sh
```
Or as a one-liner:
```bash
git clone https://github.com/yourusername/ccs.git && cd ccs && ./install.sh
```
## Configuration
Copy example config:
```bash
cp .ccs.example.json ~/.ccs.json
```
Edit `~/.ccs.json` with your profiles:
```json
{
"profiles": {
"glm": "~/.claude/glm.settings.json",
"sonnet": "~/.claude/sonnet.settings.json"
}
}
```
## Usage
### Basic
```bash
ccs glm # Use GLM profile
ccs sonnet # Use Sonnet profile
```
### Pass Arguments Transparently
All arguments after the profile name are passed directly to Claude:
```bash
ccs glm --verbose
ccs sonnet /plan "add feature"
ccs opus --model claude-opus-4
```
## Requirements
- `bash` (3.2+ compatible)
- `jq` (JSON processor)
- [Claude CLI](https://docs.claude.com/en/docs/claude-code/installation) installed
### Installing jq
```bash
# macOS
brew install jq
# Ubuntu/Debian
sudo apt install jq
# Fedora
sudo dnf install 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.
### 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.
### jq not installed
```bash
Error: jq is required but not installed
```
**Solution**: Install jq using your package manager (see Requirements section).
### PATH not set
```bash
⚠️ Warning: ~/.local/bin is not in PATH
```
**Solution**: Add to your shell profile (`~/.bashrc` or `~/.zshrc`):
```bash
export PATH="$HOME/.local/bin:$PATH"
```
Then restart your shell or run `source ~/.bashrc` (or `~/.zshrc`).
## Use Cases
### Claude Subscription + GLM Coding Plan
If you have both Claude subscription and GLM coding plan:
```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"
}
}
```
## Uninstallation
```bash
rm ~/.local/bin/ccs
rm ~/.ccs.json
```
## License
MIT
## Contributing
PRs welcome! Keep it simple (KISS principle).
## Philosophy
**YAGNI**: We don't add features "just in case"
**KISS**: Simple bash script, no complexity
**DRY**: One source of truth (config file)
This tool does ONE thing well: map profile names to settings files. Nothing more.
Executable
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
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
# 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 "See .ccs.example.json for template."
exit 1
fi
# Check jq installed
if ! command -v jq &> /dev/null; then
echo "Error: jq is required but not installed"
echo ""
echo "Install jq:"
echo " macOS: brew install jq"
echo " Ubuntu: sudo apt install jq"
echo " Fedora: sudo dnf install jq"
exit 1
fi
# Validate profile name (alphanumeric, dash, underscore only)
if [[ "$PROFILE" =~ [^a-zA-Z0-9_-] ]]; then
echo "Error: Invalid profile name. Use only alphanumeric characters, dash, or underscore."
exit 1
fi
# Validate JSON syntax
if ! jq -e . "$CONFIG_FILE" &>/dev/null; then
echo "Error: Invalid JSON in $CONFIG_FILE"
exit 1
fi
# Validate config has profiles object
if ! jq -e '.profiles' "$CONFIG_FILE" &>/dev/null; then
echo "Error: Config must have 'profiles' object"
echo "See .ccs.example.json for correct format"
exit 1
fi
# Get settings path for profile (using --arg to prevent injection)
SETTINGS_PATH=$(jq -r --arg profile "$PROFILE" '.profiles[$profile] // empty' "$CONFIG_FILE")
if [[ -z "$SETTINGS_PATH" ]]; then
echo "Error: Profile '$PROFILE' not found in $CONFIG_FILE"
echo ""
echo "Available profiles:"
jq -r '.profiles | keys[]' "$CONFIG_FILE" 2>/dev/null | sed 's/^/ - /'
exit 1
fi
# Expand ~ in path
SETTINGS_PATH="${SETTINGS_PATH/#\~/$HOME}"
# Validate settings file exists
if [[ ! -f "$SETTINGS_PATH" ]]; then
echo "Error: Settings file not found: $SETTINGS_PATH"
echo ""
echo "Create the settings file or update the path in $CONFIG_FILE"
exit 1
fi
# Shift profile arg, pass rest to claude
shift
# Execute claude with settings
exec claude --settings "$SETTINGS_PATH" "$@"
Executable
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "Installing ccs to $INSTALL_DIR..."
echo ""
# Create install dir if needed
mkdir -p "$INSTALL_DIR"
# Make executable first
chmod +x "$SCRIPT_DIR/ccs"
# Symlink ccs script
ln -sf "$SCRIPT_DIR/ccs" "$INSTALL_DIR/ccs"
# Verify installation
if [[ ! -L "$INSTALL_DIR/ccs" ]]; then
echo "❌ Error: Failed to create symlink at $INSTALL_DIR/ccs"
echo "Check directory permissions and try again."
exit 1
fi
# Check if in PATH
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
echo "⚠️ Warning: $INSTALL_DIR is not in PATH"
echo ""
echo "Add to your shell profile (~/.bashrc or ~/.zshrc):"
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
echo ""
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>"
echo ""
echo "Example:"
echo " ccs glm"
echo " ccs sonnet --verbose"