Add claude-profile engagement mode picker with statusline and session-start integration
- claude-profile: phase 1-3 picker (profile, mode, launch) with preset support, dryrun, WezTerm theming, and --append-system-prompt mode body injection - 5 mode files (chat/quick/deep/hybrid/orch) with YAML frontmatter + prose body; new escalates_to field drives statusline →Opus arrow for deep and hybrid - statusline.sh reads CLAUDE_CONFIG_DIR/active-mode.env to show [Sonnet→Opus] deep · topic format when launched via claude-profile - Root CLAUDE.md session-start: auto-selects project from cwd or CLAUDE_PROJECT in active-mode.env, skipping the interactive picker when context is clear - Spec, tests (37 assertions, 9 test files, all passing), context docs, and preset example included Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,108 +1,416 @@
|
||||
#!/usr/bin/env bash
|
||||
# claude-profile — Launch Claude Code with a named config profile
|
||||
# Profiles are directories matching ~/.claude-*/
|
||||
# claude-profile — Launch Claude Code with a profile and engagement mode
|
||||
#
|
||||
# Usage:
|
||||
# claude-profile # interactive menu
|
||||
# claude-profile <name> # direct launch (e.g. claude-profile work)
|
||||
# claude-profile # interactive: profile + mode
|
||||
# claude-profile <profile> # profile fixed, mode interactive
|
||||
# claude-profile --mode <name> # mode fixed, profile interactive
|
||||
# claude-profile --preset <name> # both from presets.yaml
|
||||
# claude-profile <profile> --mode <name> # both fixed
|
||||
# claude-profile --dryrun [...] # show resolved values, do not launch
|
||||
# claude-profile [...] -- <claude-args> # extra args passed through to claude
|
||||
#
|
||||
# Spec: specs/claude-profile.spec.md
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DEFAULT_DIR="$HOME/.claude"
|
||||
PROFILE_PATTERN="$HOME/.claude-*"
|
||||
# === Constants ===
|
||||
|
||||
# Emit a WezTerm user-var escape sequence to trigger profile-based theming.
|
||||
# WezTerm decodes the base64 value and fires a 'user-var-changed' event.
|
||||
# Safe to call in non-WezTerm terminals — the escape sequence is silently ignored.
|
||||
DEFAULT_PROFILE_DIR="$HOME/.claude"
|
||||
PROFILE_PATTERN_PREFIX="$HOME/.claude-"
|
||||
SCRIPT_PATH="$(readlink -f "$0")"
|
||||
SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
MODES_DIR="$REPO_ROOT/data/claude-profile/modes"
|
||||
|
||||
# context-load is on PATH (symlinked into ~/sbin from claude-foundations).
|
||||
# Fall back to a sibling location for testing isolation.
|
||||
if command -v context-load >/dev/null 2>&1; then
|
||||
CONTEXT_LOADER="$(command -v context-load)"
|
||||
else
|
||||
CONTEXT_LOADER="${SCRIPT_DIR}/context-load"
|
||||
fi
|
||||
|
||||
# Driver logical name -> full Claude model ID
|
||||
declare -A DRIVER_MAP=(
|
||||
[haiku]="claude-haiku-4-5-20251001"
|
||||
[sonnet]="claude-sonnet-4-6"
|
||||
[opus]="claude-opus-4-6"
|
||||
[opus-1m]="claude-opus-4-6[1m]"
|
||||
)
|
||||
|
||||
# === CLI parsing ===
|
||||
|
||||
PROFILE=""
|
||||
MODE=""
|
||||
PROJECT=""
|
||||
PRESET=""
|
||||
DRYRUN=0
|
||||
CLAUDE_ARGS=()
|
||||
|
||||
usage() {
|
||||
sed -n '2,12p' "$0" | sed 's/^# \?//'
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mode) MODE="${2:-}"; shift 2 ;;
|
||||
--preset) PRESET="${2:-}"; shift 2 ;;
|
||||
--project) PROJECT="${2:-}"; shift 2 ;;
|
||||
--dryrun|-n) DRYRUN=1; shift ;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
--) shift; CLAUDE_ARGS=("$@"); break ;;
|
||||
-*) echo "Unknown flag: $1" >&2; usage >&2; exit 1 ;;
|
||||
*)
|
||||
if [[ -z "$PROFILE" ]]; then
|
||||
PROFILE="$1"
|
||||
else
|
||||
echo "Unexpected positional argument: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Mutual exclusion
|
||||
if [[ -n "$PRESET" && ( -n "$MODE" || -n "$PROJECT" ) ]]; then
|
||||
echo "Error: --preset is mutually exclusive with --mode and --project" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# === Helpers ===
|
||||
|
||||
err() { echo "Error: $*" >&2; exit 1; }
|
||||
|
||||
dryrun_log() { printf '[dryrun] %s\n' "$*"; }
|
||||
|
||||
# Extract a key from the YAML frontmatter of a markdown file.
|
||||
# Frontmatter must be the first block, delimited by `---` lines.
|
||||
parse_frontmatter() {
|
||||
local file=$1 key=$2
|
||||
awk -v key="$key" '
|
||||
BEGIN { in_fm=0 }
|
||||
NR==1 && /^---$/ { in_fm=1; next }
|
||||
in_fm && /^---$/ { exit }
|
||||
in_fm {
|
||||
if (match($0, "^"key":[[:space:]]*")) {
|
||||
value = substr($0, RLENGTH+1)
|
||||
gsub(/^"|"$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
}
|
||||
' "$file"
|
||||
}
|
||||
|
||||
# Extract the body of a mode file (everything after the second `---`).
|
||||
mode_body() {
|
||||
local file=$1
|
||||
awk '
|
||||
/^---$/ { fm++; next }
|
||||
fm >= 2 { print }
|
||||
' "$file"
|
||||
}
|
||||
|
||||
# Read a named preset block from a presets.yaml file.
|
||||
# Output: KEY=value lines (one per field), keys uppercased and prefixed PRESET_.
|
||||
read_preset_block() {
|
||||
local file=$1 name=$2
|
||||
awk -v target="$name" '
|
||||
/^[a-zA-Z][a-zA-Z0-9_-]*:[[:space:]]*$/ {
|
||||
block_name = $0
|
||||
sub(/:[[:space:]]*$/, "", block_name)
|
||||
in_block = (block_name == target)
|
||||
next
|
||||
}
|
||||
/^[a-zA-Z]/ { in_block = 0 }
|
||||
in_block && /^[[:space:]]+[a-zA-Z]/ {
|
||||
line = $0
|
||||
sub(/^[[:space:]]+/, "", line)
|
||||
key = line; value = line
|
||||
sub(/:.*$/, "", key)
|
||||
sub(/^[^:]+:[[:space:]]*/, "", value)
|
||||
gsub(/"/, "", value)
|
||||
up = toupper(key)
|
||||
print "PRESET_" up "=" value
|
||||
}
|
||||
' "$file"
|
||||
}
|
||||
|
||||
# List available modes (sorted), printing a numbered menu.
|
||||
# Returns the array of mode names via the global MODE_LIST.
|
||||
MODE_LIST=()
|
||||
list_modes() {
|
||||
MODE_LIST=()
|
||||
local i=1
|
||||
for f in "$MODES_DIR"/*.md; do
|
||||
[[ -f "$f" ]] || continue
|
||||
local name purpose
|
||||
name=$(basename "$f" .md)
|
||||
purpose=$(awk '/^## Purpose$/{getline; getline; print; exit}' "$f")
|
||||
printf " %d) %-7s — %s\n" "$i" "$name" "$purpose"
|
||||
MODE_LIST+=("$name")
|
||||
((i++))
|
||||
done
|
||||
}
|
||||
|
||||
driver_to_model() {
|
||||
local d=$1
|
||||
local valid="${!DRIVER_MAP[*]}"
|
||||
if [[ -z "${DRIVER_MAP[$d]:-}" ]]; then
|
||||
err "Unknown driver '$d'. Valid: $valid"
|
||||
fi
|
||||
echo "${DRIVER_MAP[$d]}"
|
||||
}
|
||||
|
||||
resolve_profile_dir() {
|
||||
local name=$1
|
||||
if [[ "$name" == "default" ]]; then
|
||||
echo "$DEFAULT_PROFILE_DIR"
|
||||
else
|
||||
echo "${PROFILE_PATTERN_PREFIX}${name}"
|
||||
fi
|
||||
}
|
||||
|
||||
# WezTerm theming (existing behaviour, unchanged)
|
||||
set_wezterm_profile() {
|
||||
local profile_name="$1"
|
||||
printf '\e]1337;SetUserVar=%s=%s\a' CLAUDE_PROFILE "$(printf '%s' "$profile_name" | base64)"
|
||||
}
|
||||
|
||||
# Reset WezTerm theme when the script exits (e.g., Claude session ends)
|
||||
reset_wezterm_profile() {
|
||||
printf '\e]1337;SetUserVar=%s=%s\a' CLAUDE_PROFILE "$(printf '%s' '_reset' | base64)"
|
||||
}
|
||||
trap reset_wezterm_profile EXIT
|
||||
|
||||
# Sync all repos before starting a session
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SYNC_SCRIPT="${SCRIPT_DIR}/sync-repos"
|
||||
if [[ -x "$SYNC_SCRIPT" ]]; then
|
||||
echo "Syncing repos..."
|
||||
"$SYNC_SCRIPT" || echo "Warning: some repos failed to sync (continuing anyway)"
|
||||
# === Phase 1: Profile selection ===
|
||||
|
||||
# Sanity check on the modes directory before doing any picker work.
|
||||
[[ -d "$MODES_DIR" ]] || err "Mode files directory not found at $MODES_DIR"
|
||||
|
||||
if [[ -n "$PRESET" ]]; then
|
||||
# Find the preset across all profile presets.yaml files.
|
||||
preset_profile_dir=""
|
||||
preset_lines=""
|
||||
for candidate in "$DEFAULT_PROFILE_DIR" ${PROFILE_PATTERN_PREFIX}*; do
|
||||
[[ -d "$candidate" ]] || continue
|
||||
local_presets="$candidate/presets.yaml"
|
||||
[[ -f "$local_presets" ]] || continue
|
||||
if grep -q "^${PRESET}:[[:space:]]*$" "$local_presets"; then
|
||||
preset_profile_dir="$candidate"
|
||||
preset_lines=$(read_preset_block "$local_presets" "$PRESET")
|
||||
break
|
||||
fi
|
||||
done
|
||||
[[ -n "$preset_profile_dir" ]] || err "Preset '$PRESET' not found in any presets.yaml"
|
||||
|
||||
# Apply preset values (PRESET_PROFILE, PRESET_MODE, etc.)
|
||||
while IFS='=' read -r k v; do
|
||||
[[ -z "$k" ]] && continue
|
||||
case "$k" in
|
||||
PRESET_PROFILE) preset_profile_name="$v" ;;
|
||||
PRESET_MODE) MODE="$v" ;;
|
||||
PRESET_PROJECT) PROJECT="$v" ;;
|
||||
PRESET_TIME_HORIZON) TIME_HORIZON="$v" ;;
|
||||
PRESET_ASYNC) ASYNC_OVERRIDE="$v" ;;
|
||||
PRESET_AUTOLOOP) AUTOLOOP_OVERRIDE="$v" ;;
|
||||
esac
|
||||
done <<< "$preset_lines"
|
||||
|
||||
[[ -n "${preset_profile_name:-}" ]] || err "Preset '$PRESET' is missing required field: profile"
|
||||
|
||||
# If user also gave a positional profile, it must match
|
||||
if [[ -n "$PROFILE" && "$PROFILE" != "$preset_profile_name" ]]; then
|
||||
err "Profile mismatch: positional '$PROFILE', preset specifies '$preset_profile_name'"
|
||||
fi
|
||||
PROFILE="$preset_profile_name"
|
||||
target=$(resolve_profile_dir "$PROFILE")
|
||||
elif [[ -n "$PROFILE" ]]; then
|
||||
target=$(resolve_profile_dir "$PROFILE")
|
||||
else
|
||||
# Interactive profile picker
|
||||
echo "Claude Code profiles:"
|
||||
echo ""
|
||||
echo " 1) default ($DEFAULT_PROFILE_DIR)"
|
||||
profiles=("default")
|
||||
i=2
|
||||
for dir in ${PROFILE_PATTERN_PREFIX}*; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="${dir##*/.claude-}"
|
||||
echo " $i) $name (~/.claude-$name)"
|
||||
profiles+=("$name")
|
||||
((i++))
|
||||
done
|
||||
echo ""
|
||||
if [[ -t 0 ]]; then
|
||||
read -rp "Choose profile [1]: " choice
|
||||
else
|
||||
choice=""
|
||||
fi
|
||||
choice="${choice:-1}"
|
||||
if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#profiles[@]} )); then
|
||||
err "Invalid profile choice: $choice"
|
||||
fi
|
||||
PROFILE="${profiles[$((choice - 1))]}"
|
||||
target=$(resolve_profile_dir "$PROFILE")
|
||||
fi
|
||||
|
||||
# Gather profiles
|
||||
profiles=()
|
||||
for dir in $PROFILE_PATTERN; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="${dir##*/.claude-}"
|
||||
profiles+=("$name")
|
||||
done
|
||||
[[ -d "$target" ]] || err "Profile directory not found: $target"
|
||||
|
||||
# Direct invocation with a profile name
|
||||
if [[ ${1:-} ]]; then
|
||||
target="$HOME/.claude-$1"
|
||||
if [[ -d "$target" ]]; then
|
||||
export CLAUDE_CONFIG_DIR="$target"
|
||||
set_wezterm_profile "$1"
|
||||
echo "Using profile: $1 ($target)"
|
||||
claude "${@:2}"
|
||||
exit $?
|
||||
# === Phase 2: Mode selection ===
|
||||
|
||||
if [[ -z "$MODE" ]]; then
|
||||
# Determine default from last-mode file
|
||||
last_mode_file="$target/last-mode"
|
||||
default_mode="quick"
|
||||
if [[ -f "$last_mode_file" ]]; then
|
||||
candidate=$(cat "$last_mode_file")
|
||||
if [[ -f "$MODES_DIR/$candidate.md" ]]; then
|
||||
default_mode="$candidate"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Engagement modes:"
|
||||
list_modes
|
||||
echo ""
|
||||
if [[ -t 0 ]]; then
|
||||
read -rp "Choose mode [$default_mode]: " mode_choice
|
||||
else
|
||||
echo "Profile '$1' not found. Create it with: mkdir -p $target" >&2
|
||||
exit 1
|
||||
mode_choice=""
|
||||
fi
|
||||
mode_choice="${mode_choice:-$default_mode}"
|
||||
|
||||
if [[ "$mode_choice" =~ ^[0-9]+$ ]]; then
|
||||
if (( mode_choice < 1 || mode_choice > ${#MODE_LIST[@]} )); then
|
||||
err "Invalid mode choice: $mode_choice"
|
||||
fi
|
||||
MODE="${MODE_LIST[$((mode_choice - 1))]}"
|
||||
else
|
||||
MODE="$mode_choice"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build menu
|
||||
echo "Claude Code profiles:"
|
||||
echo ""
|
||||
i=1
|
||||
echo " $i) default (~/.claude)"
|
||||
options=("default")
|
||||
for name in "${profiles[@]}"; do
|
||||
((i++))
|
||||
echo " $i) $name (~/.claude-$name)"
|
||||
options+=("$name")
|
||||
mode_file="$MODES_DIR/$MODE.md"
|
||||
[[ -f "$mode_file" ]] || err "Mode file not found: $mode_file"
|
||||
|
||||
# Parse frontmatter
|
||||
DRIVER=$(parse_frontmatter "$mode_file" "driver")
|
||||
TAG=$(parse_frontmatter "$mode_file" "tag")
|
||||
ASYNC_OK=$(parse_frontmatter "$mode_file" "async_ok")
|
||||
AUTOLOOP=$(parse_frontmatter "$mode_file" "autoloop")
|
||||
PLAN_MODE_AUTO=$(parse_frontmatter "$mode_file" "plan_mode_auto")
|
||||
SPEC_DRIVEN=$(parse_frontmatter "$mode_file" "spec_driven")
|
||||
ESCALATES_TO=$(parse_frontmatter "$mode_file" "escalates_to")
|
||||
|
||||
# Validate required frontmatter fields
|
||||
for field_name in DRIVER TAG ASYNC_OK; do
|
||||
if [[ -z "${!field_name}" ]]; then
|
||||
err "Mode file $mode_file is missing required frontmatter field: $(echo "$field_name" | tr '[:upper:]' '[:lower:]')"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
read -rp "Choose profile [1]: " choice
|
||||
choice="${choice:-1}"
|
||||
|
||||
# Validate choice
|
||||
if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#options[@]} )); then
|
||||
echo "Invalid choice." >&2
|
||||
exit 1
|
||||
# Mode-specific interactive questions (skipped if preset)
|
||||
if [[ -z "$PRESET" ]]; then
|
||||
case "$MODE" in
|
||||
deep|hybrid|orch)
|
||||
if [[ -z "${TIME_HORIZON:-}" ]]; then
|
||||
if [[ -t 0 ]]; then
|
||||
read -rp "Time horizon (minutes/hours/overnight) [hours]: " th
|
||||
else
|
||||
th=""
|
||||
fi
|
||||
TIME_HORIZON="${th:-hours}"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
selected="${options[$((choice - 1))]}"
|
||||
# Apply preset overrides for async/autoloop
|
||||
[[ -n "${ASYNC_OVERRIDE:-}" ]] && ASYNC_OK="$ASYNC_OVERRIDE"
|
||||
[[ -n "${AUTOLOOP_OVERRIDE:-}" ]] && {
|
||||
if [[ "$AUTOLOOP_OVERRIDE" == "yes" || "$AUTOLOOP_OVERRIDE" == "no" ]]; then
|
||||
: # leave AUTOLOOP as the mode default unless explicitly disabled
|
||||
[[ "$AUTOLOOP_OVERRIDE" == "no" ]] && AUTOLOOP="none"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "$selected" == "default" ]]; then
|
||||
echo "Using default profile ($DEFAULT_DIR)"
|
||||
export CLAUDE_CONFIG_DIR="$DEFAULT_DIR"
|
||||
set_wezterm_profile "default"
|
||||
else
|
||||
target="$HOME/.claude-$selected"
|
||||
echo "Using profile: $selected ($target)"
|
||||
export CLAUDE_CONFIG_DIR="$target"
|
||||
set_wezterm_profile "$selected"
|
||||
# === Phase 3: Launch ===
|
||||
|
||||
MODEL_ID=$(driver_to_model "$DRIVER")
|
||||
|
||||
# Validate context-load exists
|
||||
[[ -x "$CONTEXT_LOADER" ]] || err "context-load not found at $CONTEXT_LOADER"
|
||||
|
||||
# Build active-mode.env content
|
||||
active_env=$(cat <<EOF
|
||||
CLAUDE_MODE=$MODE
|
||||
CLAUDE_MODE_TAG=$TAG
|
||||
CLAUDE_DRIVER=$DRIVER
|
||||
CLAUDE_ESCALATES_TO=${ESCALATES_TO:-none}
|
||||
CLAUDE_PROJECT=${PROJECT:-}
|
||||
CLAUDE_TIME_HORIZON=${TIME_HORIZON:-}
|
||||
CLAUDE_ASYNC_OK=$ASYNC_OK
|
||||
CLAUDE_AUTOLOOP=$AUTOLOOP
|
||||
CLAUDE_PLAN_MODE_AUTO=$PLAN_MODE_AUTO
|
||||
CLAUDE_SPEC_DRIVEN=$SPEC_DRIVEN
|
||||
CLAUDE_MODE_FILE=$mode_file
|
||||
EOF
|
||||
)
|
||||
|
||||
# Mode body (everything after the frontmatter)
|
||||
body=$(mode_body "$mode_file")
|
||||
if [[ -z "$body" ]]; then
|
||||
err "Mode file has no body — nothing to load into the system prompt: $mode_file"
|
||||
fi
|
||||
|
||||
# Load the context
|
||||
CONTEXT_LOADER="${SCRIPT_DIR}/context-load"
|
||||
# Pre-flight context-load (do this even in dryrun so length numbers are accurate,
|
||||
# but suppress side effects — context-load is read-only).
|
||||
context=$("$CONTEXT_LOADER")
|
||||
|
||||
if [[ ! -x "$CONTEXT_LOADER" ]]; then
|
||||
echo "Error: context-load not found at $CONTEXT_LOADER" >&2
|
||||
exit 1
|
||||
combined_context="${context}
|
||||
|
||||
# === Engagement Mode: ${MODE} ===
|
||||
|
||||
${body}"
|
||||
|
||||
if (( DRYRUN )); then
|
||||
dryrun_log "Profile: $PROFILE ($target)"
|
||||
dryrun_log "Mode: $MODE"
|
||||
dryrun_log "Mode file: $mode_file"
|
||||
dryrun_log "Driver: $DRIVER"
|
||||
dryrun_log "Driver model: $MODEL_ID"
|
||||
dryrun_log "Project: ${PROJECT:-<not set, will be picked by CLAUDE.md>}"
|
||||
dryrun_log "Time horizon: ${TIME_HORIZON:-<not asked in this mode>}"
|
||||
dryrun_log "Async OK: $ASYNC_OK"
|
||||
dryrun_log "Autoloop: $AUTOLOOP"
|
||||
dryrun_log "Escalates to: ${ESCALATES_TO:-none}"
|
||||
dryrun_log "Plan mode auto: $PLAN_MODE_AUTO"
|
||||
dryrun_log "Spec driven: $SPEC_DRIVEN"
|
||||
dryrun_log "Would write: $target/active-mode.env"
|
||||
dryrun_log "Would write: $target/last-mode"
|
||||
if (( ${#CLAUDE_ARGS[@]} > 0 )); then
|
||||
dryrun_log "Would execute: claude --model $MODEL_ID --append-system-prompt <context+mode> ${CLAUDE_ARGS[*]}"
|
||||
else
|
||||
dryrun_log "Would execute: claude --model $MODEL_ID --append-system-prompt <context+mode>"
|
||||
fi
|
||||
dryrun_log "Context length: ${#combined_context} chars (${#context} from context-load + ${#body} from mode body)"
|
||||
dryrun_log "No changes made."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
context="$("$CONTEXT_LOADER")"
|
||||
# === Real launch from here on ===
|
||||
|
||||
if [[ -n "$context" ]]; then
|
||||
claude --append-system-prompt "$context" "$@"
|
||||
else
|
||||
claude "$@"
|
||||
fi
|
||||
# Set up WezTerm theming and reset trap
|
||||
trap reset_wezterm_profile EXIT
|
||||
export CLAUDE_CONFIG_DIR="$target"
|
||||
set_wezterm_profile "$PROFILE"
|
||||
|
||||
# Disable adaptive thinking (existing behaviour)
|
||||
export CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1
|
||||
|
||||
# Write state files
|
||||
printf '%s\n' "$active_env" > "$target/active-mode.env"
|
||||
printf '%s\n' "$MODE" > "$target/last-mode"
|
||||
|
||||
echo "Launching: $PROFILE / $MODE${PROJECT:+ / $PROJECT}"
|
||||
exec claude --model "$MODEL_ID" --append-system-prompt "$combined_context" "${CLAUDE_ARGS[@]}"
|
||||
|
||||
Reference in New Issue
Block a user