#!/usr/bin/env bash # Install the DeepSeek Harness side of claude-harness. # # This script never installs the Cordis bundle into a profile: the Desktop # profile is Electron-managed, so the bundle goes through the GUI Plugins page # or the model-facing `plugin_manager` tool. The script prepares the global # instructions and the user skill root, then prints the bundle path. # # Usage: # bash dsh/install.sh --check # bash dsh/install.sh --global # bash dsh/install.sh --skills # bash dsh/install.sh --all # # Run it from a terminal, not from inside a DSH session: --global and --skills # write outside the session workspace and would require approval. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" BUNDLE="$ROOT/dsh/bundle" DSH_HOME="${DSH_HOME:-$HOME/.dsh}" DO_CHECK=0 DO_GLOBAL=0 DO_SKILLS=0 usage() { cat < install a bundle from $BUNDLE or ask the agent to call plugin_manager install_bundle with that target. EOF } if [ "$#" -eq 0 ]; then DO_CHECK=1 fi for arg in "$@"; do case "$arg" in --check) DO_CHECK=1 ;; --global) DO_GLOBAL=1 ;; --skills) DO_SKILLS=1 ;; --all) DO_CHECK=1; DO_GLOBAL=1; DO_SKILLS=1 ;; -h|--help) usage; exit 0 ;; *) echo "unknown option: $arg" >&2; usage >&2; exit 2 ;; esac done superpowers_skills() { local dir dir="$(ls -d "$HOME"/.claude/plugins/cache/claude-plugins-official/superpowers/*/skills 2>/dev/null | sort -V | tail -1 || true)" if [ -n "$dir" ] && [ -d "$dir" ]; then printf '%s\n' "$dir" return 0 fi return 1 } check() { local failed=0 for cmd in python3 git node; do if command -v "$cmd" >/dev/null 2>&1; then echo "ok: $cmd ($("$cmd" --version 2>&1 | head -1))" else echo "WARN: $cmd not found" >&2 failed=1 fi done if [ -f "$ROOT/hooks/harness_target.py" ]; then echo "ok: hooks/harness_target.py" else echo "FAIL: $ROOT/hooks/harness_target.py is missing" >&2 failed=1 fi if [ -f "$ROOT/global/CLAUDE.md" ]; then echo "ok: global/CLAUDE.md" else echo "FAIL: $ROOT/global/CLAUDE.md is missing" >&2 failed=1 fi if [ -f "$BUNDLE/index.js" ] && [ -f "$BUNDLE/cordis.patch.yml" ]; then echo "ok: bundle $BUNDLE" else echo "FAIL: bundle is incomplete: $BUNDLE" >&2 failed=1 fi if [ -f "$BUNDLE/hooks/guard-main.sh" ] && [ -f "$BUNDLE/hooks/require-plan.sh" ]; then echo "ok: hook bridge" else echo "FAIL: hook bridge is missing (is dsh/bundle/hooks a symlink to ../../hooks?)" >&2 failed=1 fi if superpowers_skills >/dev/null 2>&1; then echo "ok: superpowers skills ($(superpowers_skills))" else echo "WARN: superpowers plugin not found; the bootstrap will be skipped" >&2 fi if [ -f "$HOME/.claude/roles/dev.md" ]; then echo "ok: roles (~/.claude/roles)" elif [ -f "$ROOT/roles/dev.md" ]; then echo "ok: roles ($ROOT/roles; ~/.claude/roles is not installed)" else echo "WARN: roles not found; /role will fail until ~/.claude/roles or $ROOT/roles exists" >&2 fi echo "DSH_HOME=$DSH_HOME" return "$failed" } install_global() { mkdir -p "$DSH_HOME" local target="$DSH_HOME/AGENTS.md" if [ -f "$target" ]; then local backup="$target.bak.$(date +%s)" cp "$target" "$backup" echo "backup: $backup" fi { echo "# DeepSeek Harness global instructions" echo echo "Installed from $ROOT/global/CLAUDE.md by dsh/install.sh." echo "The dsh-harness bundle enforces guard-main/require-plan and provides /role." echo "DSH adapter: role hints come from the first message or /role; there is no SessionStart hint." echo "DSH adapter: /role reads ~/.claude/roles when installed, otherwise $ROOT/roles." echo "DSH adapter: codegraph MCP tools are named mcp__codegraph__codegraph_explore." echo cat "$ROOT/global/CLAUDE.md" } > "$target" echo "written: $target" } install_skills() { mkdir -p "$DSH_HOME/skills" local skills if ! skills="$(superpowers_skills)"; then echo "WARN: superpowers skills not found; nothing linked" >&2 return 0 fi local count=0 local dir name for dir in "$skills"/*; do [ -f "$dir/SKILL.md" ] || continue name="$(basename "$dir")" ln -sfn "$dir" "$DSH_HOME/skills/$name" count=$((count + 1)) done echo "linked $count superpowers skills into $DSH_HOME/skills" } print_bundle_step() { cat < install a bundle from the directory above; - or ask the agent to call plugin_manager install_bundle with target "$BUNDLE". Do not edit \$DSH_HOME/profiles/desktop/package.json or cordis.patch.yml by hand. Optional: add CodeGraph as an MCP server from dsh/mcp.codegraph.yml. EOF } status=0 if [ "$DO_CHECK" = 1 ]; then check || status=1 fi if [ "$DO_GLOBAL" = 1 ]; then install_global fi if [ "$DO_SKILLS" = 1 ]; then install_skills fi if [ "$DO_GLOBAL" = 1 ] || [ "$DO_SKILLS" = 1 ]; then print_bundle_step fi exit "$status"