Skip to content

Instantly share code, notes, and snippets.

@therebelrobot
Created April 22, 2026 22:22
Show Gist options
  • Select an option

  • Save therebelrobot/ac9620d918eee3a64eaa4d4f2b39c286 to your computer and use it in GitHub Desktop.

Select an option

Save therebelrobot/ac9620d918eee3a64eaa4d4f2b39c286 to your computer and use it in GitHub Desktop.
Rough and dirty migrate to kilo from roo
#!/usr/bin/env bash
# =============================================================================
# migrate-roo-to-kilo.sh
# Migrates Roo Code configuration to Kilo Code — global and/or project-level.
#
# USAGE:
# ./migrate-roo-to-kilo.sh [OPTIONS]
#
# OPTIONS:
# --global-only Migrate only global config (~/.roo/, VSCode globalStorage)
# --project-only Migrate only project-level config (.roo/, .roomodes)
# --dry-run Show what would happen without writing anything
# --no-backup Skip creating backups of existing Kilo Code config files
# --skip-existing Skip files that already exist in the Kilo Code destination
# --help Show this help message
#
# WHAT GETS MIGRATED:
#
# Global:
# ~/.roo/rules/ -> ~/.config/kilo/rules/
# + registers glob in ~/.config/kilo/kilo.jsonc
# ~/.roo/commands/ -> ~/.config/kilo/commands/ (slash-command workflows)
# ~/.roo/skills/ -> ~/.kilo/skills/
# [VSCode globalStorage]
# mcp_settings.json -> Kilo Code globalStorage mcp_settings.json
# custom_modes.yaml/json -> ~/.config/kilo/agents/<slug>.md (one per mode)
#
# Project (run from project root):
# .roo/mcp.json -> .kilo/mcp.json
# .roo/rules/ -> .kilo/rules/
# + registers glob in kilo.jsonc
# .roo/commands/ -> .kilo/commands/ (slash-command workflows)
# .roo/skills/ -> .kilo/skills/
# .roomodes -> .kilo/agents/<slug>.md (one per mode)
#
# KILO CODE PATH REFERENCE (VSCode extension):
# Global agents: ~/.config/kilo/agents/<slug>.md
# Global commands: ~/.config/kilo/commands/<name>.md
# Global rules: ~/.config/kilo/rules/<name>.md (+ kilo.jsonc instructions)
# Global skills: ~/.kilo/skills/<name>/SKILL.md
# Global MCP: [VSCode globalStorage]/kilocode.kilo-code/settings/mcp_settings.json
# Project agents: .kilo/agents/<slug>.md
# Project commands: .kilo/commands/<name>.md
# Project rules: .kilo/rules/<name>.md (+ kilo.jsonc instructions)
# Project skills: .kilo/skills/<name>/SKILL.md
# Project MCP: .kilo/mcp.json
#
# REQUIREMENTS:
# - bash 4+
# - python3 with PyYAML (`pip3 install pyyaml`) for .roomodes / custom_modes.yaml
# Falls back to JSON parsing if PyYAML is absent (works for custom_modes.json)
# =============================================================================
set -euo pipefail
# ── colours ──────────────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
# ── flags ─────────────────────────────────────────────────────────────────────
DO_GLOBAL=true
DO_PROJECT=true
DRY_RUN=false
BACKUP=true
SKIP_EXISTING=false
# ── counters ──────────────────────────────────────────────────────────────────
COPIED=0
SKIPPED=0
WARNINGS=0
# ── helpers ───────────────────────────────────────────────────────────────────
log_info() { echo -e "${BLUE}[INFO]${RESET} $*"; }
log_ok() { echo -e "${GREEN}[OK]${RESET} $*"; }
log_skip() { echo -e "${YELLOW}[SKIP]${RESET} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; WARNINGS=$((WARNINGS+1)); }
log_dry() { echo -e "${CYAN}[DRY]${RESET} $*"; }
log_section() { echo -e "\n${BOLD}${BLUE}== $* ==${RESET}"; }
die() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; }
usage() {
sed -n '/^# USAGE/,/^# ====/p' "$0" | grep -v '^# ====' | sed 's/^# \?//'
exit 0
}
# ── arg parsing ───────────────────────────────────────────────────────────────
for arg in "$@"; do
case "$arg" in
--global-only) DO_PROJECT=false ;;
--project-only) DO_GLOBAL=false ;;
--dry-run) DRY_RUN=true ;;
--no-backup) BACKUP=false ;;
--skip-existing) SKIP_EXISTING=true ;;
--help|-h) usage ;;
*) die "Unknown option: $arg. Use --help for usage." ;;
esac
done
# ── platform detection ────────────────────────────────────────────────────────
OS="$(uname -s)"
case "$OS" in
Darwin)
VSCODE_GLOBAL_STORAGE="$HOME/Library/Application Support/Code/User/globalStorage"
;;
Linux)
VSCODE_GLOBAL_STORAGE="${XDG_DATA_HOME:-$HOME/.local/share}/Code/User/globalStorage"
;;
MINGW*|MSYS*|CYGWIN*)
VSCODE_GLOBAL_STORAGE="${APPDATA}/Code/User/globalStorage"
;;
*)
log_warn "Unknown OS '$OS'; assuming Linux-style paths for VSCode storage"
VSCODE_GLOBAL_STORAGE="$HOME/.local/share/Code/User/globalStorage"
;;
esac
# Roo Code paths
ROO_VSCODE_DIR="$VSCODE_GLOBAL_STORAGE/rooveterinaryinc.roo-cline/settings"
ROO_GLOBAL_DIR="$HOME/.roo"
# Kilo Code paths
KILO_VSCODE_DIR="$VSCODE_GLOBAL_STORAGE/kilocode.kilo-code/settings"
KILO_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/kilo" # rules, commands, agents, kilo.jsonc
KILO_HOME_DIR="$HOME/.kilo" # skills
PROJECT_ROO_DIR=".roo"
PROJECT_KILO_DIR=".kilo"
# Built-in Kilo agent slugs to skip during modes migration
BUILTIN_SLUGS=("code" "build" "architect" "plan" "ask" "debug" "orchestrator" "explore" "general")
# Temp dir for Python helper script (cleaned up on exit)
TMPDIR_SCRIPT="$(mktemp -d)"
PYTHON_HELPER="$TMPDIR_SCRIPT/modes_converter.py"
cleanup() { rm -rf "$TMPDIR_SCRIPT"; }
trap cleanup EXIT
# ── write Python helper to tempfile ──────────────────────────────────────────
write_python_helper() {
cat > "$PYTHON_HELPER" << 'PYEOF'
#!/usr/bin/env python3
"""
Roo modes -> Kilo agents converter.
Usage: python3 modes_converter.py <yaml_file> <output_dir> <dry_run> <skip_existing> [builtin_slug ...]
Writes one <slug>.md file per custom mode into output_dir.
Prints __RESULT__ copied=N skipped=N to stdout on completion.
All other messages go to stderr.
"""
import sys
import os
import re
yaml_file = sys.argv[1]
output_dir = sys.argv[2]
dry_run = sys.argv[3].lower() == "true"
skip_existing = sys.argv[4].lower() == "true"
builtin_slugs = set(sys.argv[5:])
# ── YAML loading ──────────────────────────────────────────────────────────────
try:
import yaml
def load_file(path):
with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
except ImportError:
import json
def load_file(path):
with open(path, "r", encoding="utf-8") as f:
raw = f.read()
for attempt in [raw, re.sub(r"//[^\n]*", "", raw)]:
try:
return json.loads(attempt)
except json.JSONDecodeError:
pass
print(
f"[WARN] PyYAML not installed and {path} is not valid JSON. "
"Install PyYAML: pip3 install pyyaml",
file=sys.stderr,
)
return None
data = load_file(yaml_file)
if data is None:
print("__RESULT__ copied=0 skipped=0")
sys.exit(0)
modes = []
if isinstance(data, dict):
modes = data.get("customModes", [])
elif isinstance(data, list):
modes = data
if not modes:
print(f"[INFO] No customModes found in {yaml_file}", file=sys.stderr)
print("__RESULT__ copied=0 skipped=0")
sys.exit(0)
copied = 0
skipped = 0
def yaml_str(s):
"""Wrap value in YAML double-quotes, escaping internal quotes."""
if not s:
return '""'
escaped = s.replace("\\", "\\\\").replace('"', '\\"')
return f'"{escaped}"'
for mode in modes:
if not isinstance(mode, dict):
continue
slug = (mode.get("slug") or "").strip()
if not slug:
print(f"[WARN] Mode missing slug, skipping: {mode}", file=sys.stderr)
continue
if slug in builtin_slugs:
print(f"[SKIP] Built-in slug '{slug}' -> maps to Kilo built-in, skipping", file=sys.stderr)
skipped += 1
continue
description = (mode.get("description") or "").strip()
when_to_use = (mode.get("whenToUse") or "").strip()
role_definition = (mode.get("roleDefinition") or "").strip()
custom_instruct = (mode.get("customInstructions") or "").strip()
# Combine description + whenToUse
combined = description
if when_to_use and when_to_use != description:
combined = (combined + " " + when_to_use).strip() if combined else when_to_use
# Build frontmatter
fm = ["---", f"description: {yaml_str(combined)}", "mode: primary"]
# Convert groups -> permission (best-effort)
groups = mode.get("groups", [])
perm_lines = []
for g in groups:
if isinstance(g, str):
g_name, g_cfg = g, None
elif isinstance(g, list) and g:
g_name = g[0]
g_cfg = g[1] if len(g) > 1 else None
else:
continue
if g_name == "read":
perm_lines.append(" read: allow")
elif g_name == "edit":
if isinstance(g_cfg, dict) and g_cfg.get("fileRegex"):
regex = g_cfg["fileRegex"]
# Rough regex -> glob: strip anchors, unescape dots
glob = regex.replace("\\.", ".").rstrip("$").lstrip("^")
perm_lines.append(" edit:")
perm_lines.append(f' "{glob}": allow')
perm_lines.append(' "*": deny')
else:
perm_lines.append(" edit: allow")
elif g_name == "command":
perm_lines.append(" bash: allow")
# mcp, browser -> no direct Kilo permission mapping needed
if perm_lines:
fm.append("permission:")
fm.extend(perm_lines)
fm.append("---")
frontmatter = "\n".join(fm)
# Build markdown body
body_parts = []
if role_definition:
body_parts.append(role_definition)
if custom_instruct:
body_parts.append("\n## Custom Instructions\n")
body_parts.append(custom_instruct)
body = "\n".join(body_parts).strip()
content = frontmatter + "\n" + body + "\n"
dest = os.path.join(output_dir, f"{slug}.md")
if dry_run:
print(f"[DRY] would write -> {dest}", file=sys.stderr)
copied += 1
continue
if os.path.exists(dest) and skip_existing:
print(f"[SKIP] Exists: {dest}", file=sys.stderr)
skipped += 1
continue
os.makedirs(output_dir, exist_ok=True)
with open(dest, "w", encoding="utf-8") as f:
f.write(content)
print(f"[OK] wrote -> {dest}", file=sys.stderr)
copied += 1
print(f"__RESULT__ copied={copied} skipped={skipped}")
PYEOF
}
# ── dependency checks ─────────────────────────────────────────────────────────
check_python() {
if ! command -v python3 &>/dev/null; then
die "python3 is required for YAML parsing of .roomodes / custom_modes.yaml."
fi
}
# ── core copy helpers ─────────────────────────────────────────────────────────
copy_file() {
local src="$1"
local dest="$2"
if [[ ! -f "$src" ]]; then
log_warn "Source not found, skipping: $src"
return
fi
if [[ "$DRY_RUN" == true ]]; then
log_dry "cp $src -> $dest"
COPIED=$((COPIED+1))
return
fi
mkdir -p "$(dirname "$dest")"
if [[ -f "$dest" ]]; then
if [[ "$SKIP_EXISTING" == true ]]; then
log_skip "Exists: $dest"
SKIPPED=$((SKIPPED+1))
return
fi
if [[ "$BACKUP" == true ]]; then
local bak="${dest}.roo-backup-$(date +%Y%m%d-%H%M%S)"
cp "$dest" "$bak"
log_info " backed up -> $bak"
fi
fi
cp "$src" "$dest"
log_ok "$src -> $dest"
COPIED=$((COPIED+1))
}
copy_dir() {
local src_dir="$1"
local dest_dir="$2"
if [[ ! -d "$src_dir" ]]; then
log_warn "Source dir not found, skipping: $src_dir"
return
fi
while IFS= read -r -d '' src_file; do
[[ "$(basename "$src_file")" == ".DS_Store" ]] && continue
local rel="${src_file#$src_dir/}"
copy_file "$src_file" "$dest_dir/$rel"
done < <(find "$src_dir" -type f -print0 2>/dev/null)
}
# ── kilo.jsonc helpers ────────────────────────────────────────────────────────
# Generates or merges the instructions array in a kilo.jsonc file.
# upsert_kilo_jsonc <jsonc_file> <glob_pattern>
upsert_kilo_jsonc() {
local jsonc_file="$1"
local glob_pattern="$2"
if [[ "$DRY_RUN" == true ]]; then
log_dry "upsert instructions: \"$glob_pattern\" in $jsonc_file"
return
fi
mkdir -p "$(dirname "$jsonc_file")"
if [[ ! -f "$jsonc_file" ]]; then
printf '{\n "instructions": ["%s"]\n}\n' "$glob_pattern" > "$jsonc_file"
log_ok "created $jsonc_file with instructions: [\"$glob_pattern\"]"
return
fi
# File exists — check if pattern is already present
if grep -qF "\"$glob_pattern\"" "$jsonc_file" 2>/dev/null; then
log_info " $jsonc_file already contains \"$glob_pattern\""
return
fi
# Inject into existing instructions array using python3
python3 - "$jsonc_file" "$glob_pattern" << 'PYJSONC'
import sys, re, json
path = sys.argv[1]
pattern = sys.argv[2]
with open(path, "r", encoding="utf-8") as f:
raw = f.read()
# Strip single-line comments for parsing (JSONC)
stripped = re.sub(r"//[^\n]*", "", raw)
try:
data = json.loads(stripped)
except json.JSONDecodeError as e:
print(f"[WARN] Could not parse {path} as JSONC: {e}", file=sys.stderr)
print(f"[WARN] Please manually add \"{pattern}\" to the instructions array.", file=sys.stderr)
sys.exit(0)
instructions = data.get("instructions", [])
if pattern not in instructions:
instructions.append(pattern)
data["instructions"] = instructions
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
f.write("\n")
print(f"[OK] updated {path}: added \"{pattern}\" to instructions")
else:
print(f"[INFO] {path} already contains \"{pattern}\"")
PYJSONC
}
# ── modes -> agents conversion ────────────────────────────────────────────────
# run_modes_conversion <yaml_file> <output_dir> <label>
run_modes_conversion() {
local yaml_file="$1"
local output_dir="$2"
local label="$3"
if [[ ! -f "$yaml_file" ]]; then
log_warn "Modes file not found, skipping: $yaml_file"
return
fi
check_python
log_info "$label"
# Write Python helper to tempfile if not already done
if [[ ! -f "$PYTHON_HELPER" ]]; then
write_python_helper
fi
# Run the helper; capture its stdout (result line), let stderr flow to terminal
local result_line
result_line="$(python3 "$PYTHON_HELPER" \
"$yaml_file" "$output_dir" "$DRY_RUN" "$SKIP_EXISTING" \
"${BUILTIN_SLUGS[@]}" 2>&1 1>/tmp/kilo_modes_result.txt | cat >&2; cat /tmp/kilo_modes_result.txt)"
if [[ -n "$result_line" ]]; then
local c s
c="$(echo "$result_line" | grep -oE 'copied=[0-9]+' | grep -oE '[0-9]+' || echo 0)"
s="$(echo "$result_line" | grep -oE 'skipped=[0-9]+' | grep -oE '[0-9]+' || echo 0)"
COPIED=$((COPIED + ${c:-0}))
SKIPPED=$((SKIPPED + ${s:-0}))
fi
}
# ── section: global migration ─────────────────────────────────────────────────
migrate_global() {
log_section "GLOBAL MIGRATION"
if [[ ! -d "$ROO_GLOBAL_DIR" ]]; then
log_warn "Global Roo directory not found: $ROO_GLOBAL_DIR"
else
# rules -> ~/.config/kilo/rules/ + register in global kilo.jsonc
if [[ -d "$ROO_GLOBAL_DIR/rules" ]]; then
log_info "Global rules: $ROO_GLOBAL_DIR/rules/ -> $KILO_CONFIG_DIR/rules/"
copy_dir "$ROO_GLOBAL_DIR/rules" "$KILO_CONFIG_DIR/rules"
upsert_kilo_jsonc "$KILO_CONFIG_DIR/kilo.jsonc" "~/.config/kilo/rules/*.md"
fi
# rules-*/ (mode-specific rule dirs)
while IFS= read -r -d '' rules_dir; do
local dir_name
dir_name="$(basename "$rules_dir")"
log_info "Mode-specific global rules: $rules_dir -> $KILO_CONFIG_DIR/$dir_name/"
copy_dir "$rules_dir" "$KILO_CONFIG_DIR/$dir_name"
done < <(find "$ROO_GLOBAL_DIR" -maxdepth 1 -type d -name 'rules-*' -print0 2>/dev/null)
# commands -> ~/.config/kilo/commands/ (Kilo "slash-command workflows")
if [[ -d "$ROO_GLOBAL_DIR/commands" ]]; then
log_info "Global commands: $ROO_GLOBAL_DIR/commands/ -> $KILO_CONFIG_DIR/commands/"
copy_dir "$ROO_GLOBAL_DIR/commands" "$KILO_CONFIG_DIR/commands"
fi
# skills -> ~/.kilo/skills/
if [[ -d "$ROO_GLOBAL_DIR/skills" ]]; then
log_info "Global skills: $ROO_GLOBAL_DIR/skills/ -> $KILO_HOME_DIR/skills/"
copy_dir "$ROO_GLOBAL_DIR/skills" "$KILO_HOME_DIR/skills"
fi
fi
# VSCode globalStorage: MCP settings
if [[ -f "$ROO_VSCODE_DIR/mcp_settings.json" ]]; then
log_info "Global MCP settings -> $KILO_VSCODE_DIR/mcp_settings.json"
[[ "$DRY_RUN" == false ]] && mkdir -p "$KILO_VSCODE_DIR"
copy_file "$ROO_VSCODE_DIR/mcp_settings.json" "$KILO_VSCODE_DIR/mcp_settings.json"
else
log_warn "Global MCP settings not found: $ROO_VSCODE_DIR/mcp_settings.json"
fi
# VSCode globalStorage: custom modes -> ~/.config/kilo/agents/<slug>.md
local kilo_global_agents="$KILO_CONFIG_DIR/agents"
for modes_file in custom_modes.yaml custom_modes.json; do
local src="$ROO_VSCODE_DIR/$modes_file"
if [[ -f "$src" ]]; then
run_modes_conversion "$src" "$kilo_global_agents" \
"Global modes ($modes_file) -> $kilo_global_agents/<slug>.md"
fi
done
# Also process exported mode YAML files from ~/.roo/modes/
if [[ -d "$ROO_GLOBAL_DIR/modes" ]]; then
while IFS= read -r -d '' yaml_file; do
local fname
fname="$(basename "$yaml_file")"
run_modes_conversion "$yaml_file" "$kilo_global_agents" \
"Global exported mode ($fname) -> $kilo_global_agents/<slug>.md"
done < <(find "$ROO_GLOBAL_DIR/modes" -type f \( -name "*.yaml" -o -name "*.yml" -o -name "*.json" \) -print0 2>/dev/null)
fi
}
# ── section: project migration ────────────────────────────────────────────────
migrate_project() {
log_section "PROJECT MIGRATION ($(pwd))"
if [[ ! -d "$PROJECT_ROO_DIR" ]] && [[ ! -f ".roomodes" ]]; then
log_warn "No .roo/ directory or .roomodes file found in current directory."
log_warn "Run this script from the root of a Roo Code project."
return
fi
local project_jsonc="kilo.jsonc"
# .roo/mcp.json -> .kilo/mcp.json
if [[ -f "$PROJECT_ROO_DIR/mcp.json" ]]; then
log_info "Project MCP: .roo/mcp.json -> .kilo/mcp.json"
copy_file "$PROJECT_ROO_DIR/mcp.json" "$PROJECT_KILO_DIR/mcp.json"
fi
# .roo/rules/ -> .kilo/rules/ + register in kilo.jsonc
if [[ -d "$PROJECT_ROO_DIR/rules" ]]; then
log_info "Project rules: .roo/rules/ -> .kilo/rules/"
copy_dir "$PROJECT_ROO_DIR/rules" "$PROJECT_KILO_DIR/rules"
upsert_kilo_jsonc "$project_jsonc" ".kilo/rules/*.md"
fi
# .roo/rules-*/ -> .kilo/rules-*/
while IFS= read -r -d '' rules_dir; do
local dir_name
dir_name="$(basename "$rules_dir")"
log_info "Mode-specific project rules: $rules_dir -> $PROJECT_KILO_DIR/$dir_name/"
copy_dir "$rules_dir" "$PROJECT_KILO_DIR/$dir_name"
done < <(find "$PROJECT_ROO_DIR" -maxdepth 1 -type d -name 'rules-*' -print0 2>/dev/null)
# .roo/commands/ -> .kilo/commands/ (Kilo slash-command workflows)
if [[ -d "$PROJECT_ROO_DIR/commands" ]]; then
log_info "Project commands: .roo/commands/ -> .kilo/commands/"
copy_dir "$PROJECT_ROO_DIR/commands" "$PROJECT_KILO_DIR/commands"
fi
# .roo/skills/ -> .kilo/skills/
if [[ -d "$PROJECT_ROO_DIR/skills" ]]; then
log_info "Project skills: .roo/skills/ -> .kilo/skills/"
copy_dir "$PROJECT_ROO_DIR/skills" "$PROJECT_KILO_DIR/skills"
fi
# .roomodes -> .kilo/agents/<slug>.md (one per mode)
if [[ -f ".roomodes" ]]; then
run_modes_conversion ".roomodes" "$PROJECT_KILO_DIR/agents" \
".roomodes -> .kilo/agents/<slug>.md"
fi
}
# ── summary ───────────────────────────────────────────────────────────────────
print_summary() {
echo ""
echo -e "${BOLD}== MIGRATION SUMMARY ==${RESET}"
[[ "$DRY_RUN" == true ]] && echo -e " ${CYAN}Mode:${RESET} DRY RUN (no files written)"
echo -e " ${GREEN}Copied:${RESET} $COPIED file(s)"
echo -e " ${YELLOW}Skipped:${RESET} $SKIPPED file(s)"
[[ $WARNINGS -gt 0 ]] && echo -e " ${YELLOW}Warnings:${RESET} $WARNINGS (see above)"
echo ""
if [[ "$DRY_RUN" == false && $COPIED -gt 0 ]]; then
echo -e "${GREEN}Migration complete!${RESET}"
echo ""
echo -e "${BOLD}Kilo Code config locations:${RESET}"
if [[ "$DO_GLOBAL" == true ]]; then
echo " Global rules: $KILO_CONFIG_DIR/rules/"
echo " Global commands: $KILO_CONFIG_DIR/commands/"
echo " Global agents: $KILO_CONFIG_DIR/agents/"
echo " Global skills: $KILO_HOME_DIR/skills/"
echo " Global config: $KILO_CONFIG_DIR/kilo.jsonc"
echo " Global MCP: $KILO_VSCODE_DIR/mcp_settings.json"
fi
if [[ "$DO_PROJECT" == true ]]; then
echo " Project rules: .kilo/rules/"
echo " Project commands: .kilo/commands/"
echo " Project agents: .kilo/agents/"
echo " Project skills: .kilo/skills/"
echo " Project MCP: .kilo/mcp.json"
echo " Project config: kilo.jsonc"
fi
echo ""
echo -e "${BOLD}Next steps:${RESET}"
echo " 1. Reload VS Code (Cmd+Shift+P -> 'Developer: Reload Window')."
echo " 2. Open Kilo Code and verify agents appear in the agent picker."
echo " 3. Check kilo.jsonc 'instructions' array lists your rule files."
echo " 4. Review .kilo/agents/*.md — permissions are best-effort"
echo " conversions from Roo groups; adjust as needed."
echo " 5. Invoke commands with /command-name in Kilo Code chat."
echo " 6. Your original Roo Code config is untouched."
[[ "$BACKUP" == true ]] && echo " 7. Backup files (*.roo-backup-*) were created for pre-existing files."
echo ""
echo -e "${BOLD}Skipped built-in slugs:${RESET} ${BUILTIN_SLUGS[*]}"
echo " These map to Kilo's built-in agents. Override via kilo.jsonc if needed."
fi
}
# ── main ──────────────────────────────────────────────────────────────────────
main() {
echo -e "${BOLD}Roo Code -> Kilo Code Migration Script${RESET}"
echo "OS: $OS"
echo "VSCode storage: $VSCODE_GLOBAL_STORAGE"
echo "Kilo config dir: $KILO_CONFIG_DIR"
echo "Kilo home dir: $KILO_HOME_DIR (skills)"
[[ "$DRY_RUN" == true ]] && echo -e "${CYAN}[DRY RUN MODE - no files will be written]${RESET}"
echo ""
[[ "$DO_GLOBAL" == true ]] && migrate_global
[[ "$DO_PROJECT" == true ]] && migrate_project
print_summary
}
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment