install-context.sh 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #!/usr/bin/env bash
  2. # install-context.sh — Download OAC context files to .claude/context/
  3. #
  4. # Requirements: bash, git (nothing else to install)
  5. #
  6. # Run from project root:
  7. # bash install-context.sh [--profile=standard] [--force] [--dry-run]
  8. set -euo pipefail
  9. GITHUB_REPO="darrenhinde/OpenAgentsControl"
  10. GITHUB_BRANCH="main"
  11. CONTEXT_SOURCE_PATH=".opencode/context"
  12. # Paths are set in main() after parsing --global flag
  13. PROJECT_ROOT="$(pwd)"
  14. GLOBAL_ROOT="${HOME}/.claude"
  15. # Colors
  16. RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
  17. log_info() { echo -e "${BLUE}ℹ${NC} $*"; }
  18. log_success() { echo -e "${GREEN}✓${NC} $*"; }
  19. log_warning() { echo -e "${YELLOW}⚠${NC} $*"; }
  20. log_error() { echo -e "${RED}✗${NC} $*" >&2; }
  21. usage() {
  22. cat <<EOF
  23. Usage: $(basename "$0") [OPTIONS]
  24. Download OAC context files. Requirements: git (nothing else to install)
  25. OPTIONS:
  26. --profile=NAME Profile: essential, standard, extended, all (default: standard)
  27. --global Install to ~/.claude/context/ (all projects share it)
  28. --force Re-download even if already installed
  29. --dry-run Show what would be installed without downloading
  30. --help Show this help
  31. PROFILES:
  32. essential/standard Core context (core + openagents-repo)
  33. extended/all Full context (all categories)
  34. SCOPE:
  35. default (no flag) Installs to .claude/context/ in the current project
  36. --global Installs to ~/.claude/context/ for all projects
  37. EOF
  38. exit 0
  39. }
  40. get_categories() {
  41. case "${1:-standard}" in
  42. essential|standard|core)
  43. echo "core openagents-repo"
  44. ;;
  45. extended|all|full)
  46. echo "core openagents-repo development ui content-creation data product learning project project-intelligence"
  47. ;;
  48. *)
  49. log_error "Unknown profile: $1"
  50. log_info "Valid profiles: essential, standard, extended, all"
  51. exit 1
  52. ;;
  53. esac
  54. }
  55. check_dependencies() {
  56. if ! command -v git >/dev/null 2>&1; then
  57. log_error "git is required but not installed."
  58. log_info "Install: brew install git (Mac) or sudo apt install git (Linux)"
  59. exit 1
  60. fi
  61. }
  62. download_context() {
  63. local categories
  64. read -ra categories <<< "$1"
  65. local temp_dir
  66. temp_dir="$(mktemp -d)"
  67. # shellcheck disable=SC2064
  68. trap "rm -rf '${temp_dir}'" EXIT
  69. log_info "Cloning repository (sparse)..."
  70. git clone --depth 1 --filter=blob:none --sparse \
  71. "https://github.com/${GITHUB_REPO}.git" "${temp_dir}" --quiet 2>&1 | grep -v "^$" || true
  72. # Get commit SHA from the clone (no second clone needed)
  73. COMMIT_SHA=$(git -C "${temp_dir}" rev-parse HEAD)
  74. log_info "Configuring sparse checkout..."
  75. local sparse_paths=""
  76. for cat in "${categories[@]}"; do
  77. sparse_paths="${sparse_paths} ${CONTEXT_SOURCE_PATH}/${cat}"
  78. done
  79. sparse_paths="${sparse_paths} ${CONTEXT_SOURCE_PATH}/navigation.md"
  80. # shellcheck disable=SC2086
  81. git -C "${temp_dir}" sparse-checkout set --skip-checks ${sparse_paths} 2>/dev/null
  82. log_info "Copying context files..."
  83. mkdir -p "${CONTEXT_DIR}"
  84. local source_dir="${temp_dir}/${CONTEXT_SOURCE_PATH}"
  85. if [ ! -d "${source_dir}" ]; then
  86. log_error "Context directory not found in repository"
  87. exit 1
  88. fi
  89. cp -r "${source_dir}/"* "${CONTEXT_DIR}/"
  90. local file_count
  91. file_count=$(find "${CONTEXT_DIR}" -type f | wc -l | tr -d ' ')
  92. log_success "Downloaded ${file_count} files"
  93. }
  94. write_manifest() {
  95. local profile="$1"
  96. local categories
  97. read -ra categories <<< "$2"
  98. local commit="$3"
  99. local timestamp
  100. timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
  101. mkdir -p "$(dirname "${MANIFEST_FILE}")"
  102. # Build JSON without jq — use printf
  103. local cats_json="["
  104. local files_json="{"
  105. local first=true
  106. for cat in "${categories[@]}"; do
  107. $first || { cats_json="${cats_json},"; files_json="${files_json},"; }
  108. cats_json="${cats_json}\"${cat}\""
  109. local count=0
  110. [ -d "${CONTEXT_DIR}/${cat}" ] && count=$(find "${CONTEXT_DIR}/${cat}" -type f | wc -l | tr -d ' ')
  111. files_json="${files_json}\"${cat}\": ${count}"
  112. first=false
  113. done
  114. cats_json="${cats_json}]"
  115. files_json="${files_json}}"
  116. printf '{
  117. "version": "1.0.0",
  118. "profile": "%s",
  119. "source": {
  120. "repository": "%s",
  121. "branch": "%s",
  122. "commit": "%s",
  123. "downloaded_at": "%s"
  124. },
  125. "categories": %s,
  126. "files": %s
  127. }\n' \
  128. "${profile}" \
  129. "${GITHUB_REPO}" \
  130. "${GITHUB_BRANCH}" \
  131. "${commit}" \
  132. "${timestamp}" \
  133. "${cats_json}" \
  134. "${files_json}" \
  135. > "${MANIFEST_FILE}"
  136. log_success "Manifest created: ${MANIFEST_FILE}"
  137. }
  138. main() {
  139. local profile="standard"
  140. local global=false
  141. local force=false
  142. local dry_run=false
  143. for arg in "$@"; do
  144. case "${arg}" in
  145. --profile=*) profile="${arg#*=}" ;;
  146. --global) global=true ;;
  147. --force) force=true ;;
  148. --dry-run) dry_run=true ;;
  149. --help|-h) usage ;;
  150. *)
  151. log_error "Unknown option: ${arg}"
  152. echo ""
  153. usage
  154. ;;
  155. esac
  156. done
  157. # Set install targets based on scope
  158. local context_dir manifest_file oac_json scope_label
  159. if [ "${global}" = true ]; then
  160. context_dir="${GLOBAL_ROOT}/context"
  161. manifest_file="${GLOBAL_ROOT}/.context-manifest.json"
  162. oac_json="" # global install: no per-project .oac.json
  163. scope_label="global (~/.claude/context)"
  164. else
  165. context_dir="${PROJECT_ROOT}/.claude/context"
  166. manifest_file="${PROJECT_ROOT}/.claude/.context-manifest.json"
  167. oac_json="${PROJECT_ROOT}/.oac.json"
  168. scope_label="project (.claude/context)"
  169. fi
  170. # Export so sub-functions can use them
  171. CONTEXT_DIR="${context_dir}"
  172. MANIFEST_FILE="${manifest_file}"
  173. local categories
  174. categories=$(get_categories "${profile}")
  175. # Already installed?
  176. if [ -f "${MANIFEST_FILE}" ] && [ "${force}" = false ]; then
  177. log_warning "Context already installed at ${scope_label}. Use --force to reinstall."
  178. log_info "Manifest: ${MANIFEST_FILE}"
  179. exit 0
  180. fi
  181. check_dependencies
  182. echo ""
  183. log_info "Scope: ${scope_label}"
  184. log_info "Profile: ${profile}"
  185. log_info "Categories: ${categories}"
  186. log_info "Target: ${CONTEXT_DIR}"
  187. echo ""
  188. if [ "${dry_run}" = true ]; then
  189. log_info "Dry run — no files downloaded"
  190. exit 0
  191. fi
  192. COMMIT_SHA=""
  193. download_context "${categories}"
  194. write_manifest "${profile}" "${categories}" "${COMMIT_SHA}"
  195. # Write .oac.json for project installs so context-scout uses the fast path
  196. if [ "${global}" = false ]; then
  197. if [ ! -f "${oac_json}" ]; then
  198. printf '{\n "version": "1",\n "context": {\n "root": ".claude/context"\n }\n}\n' > "${oac_json}"
  199. log_success ".oac.json created at project root"
  200. else
  201. log_info ".oac.json already exists — skipping"
  202. fi
  203. else
  204. log_info "Global install — no .oac.json needed (discovery chain finds ~/.claude/context automatically)"
  205. fi
  206. echo ""
  207. log_success "Context installation complete!"
  208. log_info "Scope: ${scope_label}"
  209. log_info "Context: ${CONTEXT_DIR}"
  210. log_info "Manifest: ${MANIFEST_FILE}"
  211. echo ""
  212. }
  213. main "$@"