项目文件夹

文件
2026-07-13 12:29:17 +08:00

275 行
7.6 KiB
Bash
可执行文件

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
#!/usr/bin/env bash
#
# cheat-on-content / install.sh
#
# Symlinks the 15 sub-skills into Claude Code and/or Codex skill directories so
# agents can find them globally. Re-runnable safely (overwrite after confirmation).
#
# After install, in any content project directory: open Claude Code → say "初始化"
# → /cheat-init runs the onboarding.
#
# To uninstall: bash uninstall.sh
#
# Usage:
# bash install.sh # Claude Code install, symlink mode (default)
# bash install.sh --copy # Claude Code install, copy mode
# bash install.sh --codex # Codex install into ~/.codex/skills/
# bash install.sh --all # install for Claude Code and Codex
# bash install.sh --codex --copy # Codex install, copy mode
# bash install.sh --reinstall-hooks <project-dir>
# # rewrite hook scripts in an existing user project's .cheat-hooks/
# # (use after git pull when CHANGELOG mentions hook script changes;
# # does NOT touch .cheat-state.json or any user data)
set -euo pipefail
SUB_SKILLS=(
cheat-init
cheat-learn-from
cheat-seed
cheat-score
cheat-score-blind
cheat-predict
cheat-shoot
cheat-publish
cheat-retro
cheat-persona
cheat-bump
cheat-recommend
cheat-trends
cheat-status
cheat-migrate
)
CLAUDE_SKILLS=("${SUB_SKILLS[@]}")
CODEX_SKILLS=(cheat-on-content "${SUB_SKILLS[@]}")
# Resolve the directory containing THIS script (the source root) — needed early for both modes
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
MODE="symlink"
TARGET_AGENT="claude"
# --- --reinstall-hooks branch: rewrite a user project's hook scripts only ---
if [[ "${1:-}" == "--reinstall-hooks" ]]; then
PROJECT_DIR="${2:-}"
if [[ -z "$PROJECT_DIR" ]]; then
echo "❌ Usage: bash install.sh --reinstall-hooks <path-to-user-project>"
echo " The user project must already have been initialized via /cheat-init."
exit 1
fi
if [[ ! -d "$PROJECT_DIR" ]]; then
echo "❌ Project dir not found: $PROJECT_DIR"
exit 1
fi
if [[ ! -f "$PROJECT_DIR/.cheat-state.json" ]]; then
echo "❌ $PROJECT_DIR is not a cheat-on-content project (no .cheat-state.json)."
echo " Run /cheat-init in that directory first."
exit 1
fi
HOOK_DST="$PROJECT_DIR/.cheat-hooks"
mkdir -p "$HOOK_DST"
echo ""
echo "Reinstalling hook scripts in: $PROJECT_DIR"
echo " source: $SCRIPT_DIR/hooks/"
echo ""
for hook_script in prediction-immutability.sh session-start.sh log-event.sh; do
if [[ -f "$SCRIPT_DIR/hooks/$hook_script" ]]; then
cp "$SCRIPT_DIR/hooks/$hook_script" "$HOOK_DST/$hook_script"
chmod +x "$HOOK_DST/$hook_script"
echo " ✓ updated: .cheat-hooks/$hook_script"
else
echo " ⚠️ missing in source: hooks/$hook_script (skipped)"
fi
done
echo ""
echo "✅ Hook scripts reinstalled."
echo ""
echo "Note: This did NOT touch:"
echo " - .cheat-state.json (your data)"
echo " - .claude/settings.json (hook registration — should still point at .cheat-hooks/)"
echo " - rubric_notes.md / predictions/ / videos/ (your work)"
echo ""
echo "If schema also changed (CHANGELOG marks BREAKING), additionally run /cheat-migrate"
echo "in Claude Code from your project directory."
echo ""
exit 0
fi
for arg in "$@"; do
case "$arg" in
--copy)
MODE="copy"
;;
--claude)
TARGET_AGENT="claude"
;;
--codex)
TARGET_AGENT="codex"
;;
--all)
TARGET_AGENT="all"
;;
--help|-h)
sed -n '1,35p' "$0"
exit 0
;;
*)
echo "❌ Unknown argument: $arg"
echo " Usage: bash install.sh [--copy] [--claude|--codex|--all]"
exit 1
;;
esac
done
# Sanity check: confirm we're in the cheat-on-content root
if [[ ! -f "$SCRIPT_DIR/SKILL.md" ]]; then
echo "❌ Missing: $SCRIPT_DIR/SKILL.md"
echo " Are you running install.sh from the cheat-on-content root?"
exit 1
fi
for s in "${SUB_SKILLS[@]}"; do
if [[ ! -f "$SCRIPT_DIR/skills/$s/SKILL.md" ]]; then
echo "❌ Missing: $SCRIPT_DIR/skills/$s/SKILL.md"
echo " Are you running install.sh from the cheat-on-content root?"
exit 1
fi
done
skill_source() {
local skill="$1"
if [[ "$skill" == "cheat-on-content" ]]; then
echo "$SCRIPT_DIR"
else
echo "$SCRIPT_DIR/skills/$skill"
fi
}
detect_conflicts() {
local target_dir="$1"
shift
local warned=0
for s in "$@"; do
local src
src=$(skill_source "$s")
local target="$target_dir/$s"
if [[ -e "$target" || -L "$target" ]]; then
if [[ -L "$target" ]]; then
local existing
existing=$(readlink "$target")
if [[ "$existing" != "$src" ]]; then
echo "⚠️ $target already symlinked to: $existing"
warned=1
fi
else
echo "⚠️ $target exists (not a symlink) — will be overwritten"
warned=1
fi
fi
done
return "$warned"
}
install_skills() {
local label="$1"
local target_dir="$2"
shift 2
mkdir -p "$target_dir"
echo ""
echo "Installing cheat-on-content for $label (mode: $MODE)"
echo " source: $SCRIPT_DIR"
echo " target: $target_dir/"
echo ""
for s in "$@"; do
local src
src=$(skill_source "$s")
local dst="$target_dir/$s"
if [[ -e "$dst" || -L "$dst" ]]; then
rm -rf "$dst"
fi
if [[ "$MODE" == "symlink" ]]; then
ln -s "$src" "$dst"
echo " ✓ symlinked: $s"
else
cp -R "$src" "$dst"
if [[ "$s" == "cheat-on-content" ]]; then
rm -rf "$dst/.git"
fi
echo " ✓ copied: $s"
fi
done
}
WARNED=0
if [[ "$TARGET_AGENT" == "claude" || "$TARGET_AGENT" == "all" ]]; then
detect_conflicts "$HOME/.claude/skills" "${CLAUDE_SKILLS[@]}" || WARNED=1
fi
if [[ "$TARGET_AGENT" == "codex" || "$TARGET_AGENT" == "all" ]]; then
detect_conflicts "$HOME/.codex/skills" "${CODEX_SKILLS[@]}" || WARNED=1
fi
if [[ $WARNED -eq 1 ]]; then
echo ""
read -p "Continue and overwrite? (y/N) " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 1
fi
fi
if [[ "$TARGET_AGENT" == "claude" || "$TARGET_AGENT" == "all" ]]; then
install_skills "Claude Code" "$HOME/.claude/skills" "${CLAUDE_SKILLS[@]}"
fi
if [[ "$TARGET_AGENT" == "codex" || "$TARGET_AGENT" == "all" ]]; then
install_skills "Codex" "$HOME/.codex/skills" "${CODEX_SKILLS[@]}"
fi
echo ""
echo "✅ Install complete!"
echo ""
echo "Next steps:"
echo " 1. cd into your content project (or create one):"
echo " mkdir ~/my-channel && cd ~/my-channel"
echo ""
echo " 2. Open Claude Code or Codex in that directory"
echo ""
echo " 3. In the chat, say:"
echo " 初始化"
echo " (or: 初始化 cheat-on-content)"
echo ""
if [[ "$TARGET_AGENT" == "claude" || "$TARGET_AGENT" == "all" ]]; then
echo "Verify Claude install: ls -la ~/.claude/skills/ | grep cheat"
fi
if [[ "$TARGET_AGENT" == "codex" || "$TARGET_AGENT" == "all" ]]; then
echo "Verify Codex install: ls -la ~/.codex/skills/ | grep cheat"
echo "Note: restart Codex if the new skills do not appear in the current session."
fi
echo ""
if [[ "$MODE" == "symlink" ]]; then
echo "️ Mode: symlink — edits to source SKILL.md files take effect immediately."
if [[ "$TARGET_AGENT" == "codex" ]]; then
echo " To switch to frozen copy: bash install.sh --codex --copy"
elif [[ "$TARGET_AGENT" == "all" ]]; then
echo " To switch to frozen copy: bash install.sh --all --copy"
else
echo " To switch to frozen copy: bash install.sh --copy"
fi
else
echo "️ Mode: copy — frozen at install time. Re-run install.sh to update."
fi
echo ""