#!/usr/bin/env bash
set -euo pipefail

PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}}"
VERSION_FILE="$PLUGIN_ROOT/skills/hyperflow/VERSION"
HYPERFLOW_VERSION="$( [ -f "$VERSION_FILE" ] && cat "$VERSION_FILE" || echo "unknown" )"

# Detect runtime via env-var prefix
TOOL_NAME="unknown"
for pair in "CLAUDE_CODE:claude-code" "CURSOR:cursor" "OPENCODE:opencode" "CODEX:codex" "ANTIGRAVITY:antigravity"; do
  prefix="${pair%%:*}"; label="${pair##*:}"
  env | grep -q "^${prefix}_" && { TOOL_NAME="$label"; break; }
done

# Walk up from PWD to find .hyperflow/ (stop at git root or /)
find_hyperflow_dir() {
  local d="$PWD"
  while [ "$d" != "/" ]; do
    [ -d "$d/.hyperflow" ] && { echo "$d/.hyperflow"; return; }
    [ -d "$d/.git" ] && return
    d="$(dirname "$d")"
  done
}
HF_DIR="$(find_hyperflow_dir)"

if [ "$TOOL_NAME" = "codex" ]; then
  INVOKE_LINE="Hyperflow is installed. Invoke it by saying \`hyperflow <function>\` or by typing a \`/hyperflow:*\` alias."
  DIRECT_ENTRIES="## Codex function aliases

Codex loads Hyperflow as plugin skills, not as native Claude-style slash commands. Treat these user messages as aliases and run the matching skill workflow inline:

| User says | Run |
|---|---|
| \`/hyperflow:amplify\`, \`hyperflow amplify\` | \`amplify\` |
| \`/hyperflow:spec\`, \`hyperflow spec\` | \`spec\` |
| \`/hyperflow:scope\`, \`hyperflow scope\` | \`scope\` |
| \`/hyperflow:dispatch\`, \`hyperflow dispatch\` | \`dispatch\` |
| \`/hyperflow:workflow\`, \`hyperflow workflow\` | \`workflow\` (Codex portable big-task workflow adapter) |
| \`/hyperflow:trace\`, \`hyperflow trace\` | \`trace\` |
| \`/hyperflow:audit\`, \`hyperflow audit\` | \`audit\` |
| \`/hyperflow:deploy\`, \`hyperflow deploy\` | \`deploy\` |
| \`/hyperflow:cache\`, \`hyperflow cache\` | \`cache\` |
| \`/hyperflow:status\`, \`hyperflow status\` | \`status\` |
| \`/hyperflow:sticky\`, \`hyperflow sticky\` | \`sticky\` |
| \`/hyperflow:bridge\`, \`hyperflow bridge\` | \`bridge\` |
| \`/hyperflow:flush\`, \`hyperflow flush\` | \`flush\` |
| \`/hyperflow:background\`, \`hyperflow background\` | \`background\` |
| \`/hyperflow:scaffold\`, \`hyperflow scaffold\` | \`scaffold\` |

Never answer that \`/hyperflow:*\` is an unknown command in Codex. Strip the alias, load \`skills/<name>/SKILL.md\`, and follow that workflow.

## Codex subagents and auto-chain

If the runtime exposes Codex multi-agent tools, map Hyperflow \`Agent\` calls to Codex subagents: worker/searcher/writer roles use worker or explorer subagents, independent sibling workers run in parallel where possible, and Hyperflow Codex defaults use \`gpt-5.4\` with \`low\` reasoning for worker fast mode. If the callable tool is named \`multi_agent_v1.spawn_agent\`, use \`agent_type: worker\` for implementer/writer execution and \`agent_type: explorer\` for search/codebase research. Thinking roles remain \`gpt-5.5\` with adaptive \`low\` / \`medium\` / \`high\` reasoning. Never default to \`xhigh\`.

If subagent tools are unavailable in this session, emulate worker/reviewer phases inline with clear labels and continue.

For \`/hyperflow:workflow\`, use the Codex portable workflow adapter: research and planning, \`.hyperflow/tasks/\` progress tracking when needed, parallel subagents when exposed, inline worker/reviewer phases otherwise, adversarial verification, quality gates, per-task conventional commits, and final synthesis. Do not describe this as native Claude Code dynamic workflow support.

Codex may not expose Claude Code's \`Skill\` handoff tool. Treat handoffs as inline continuation: \`amplify → spec → scope → dispatch\`; selected \`audit\` / \`deploy\` follow-ups run inline; audit fix gates continue into \`scope\`. Do not stop with “Skill tool unavailable”.

If \`AskUserQuestion\` is unavailable, print required gates as concise \`Hyperflow Question\` chat blocks and wait for the user's answer."
else
  INVOKE_LINE="Hyperflow is installed. It is **not** always-on — invoke a skill explicitly when you need it."
  DIRECT_ENTRIES="## Direct entries

| Command | When to use |
|---|---|
| \`/hyperflow:scaffold\` | First-time setup — analyze project, build \`.hyperflow/\` cache, install shims |
| \`/hyperflow:spec\` | Design exploration before any code is written |
| \`/hyperflow:scope\` | Decompose a task into a worker-friendly batch file in \`.hyperflow/tasks/\` |
| \`/hyperflow:dispatch\` | Run a planned task: dispatch workers in parallel with thinking-tier reviews |
| \`/hyperflow:workflow\` | Big-task workflow lane: Claude Code native workflows; Codex/OpenCode portable adapter |
| \`/hyperflow:trace\` | Systematic root-cause analysis for any bug or test failure |
| \`/hyperflow:audit\` | Multi-level code review (L1–L5) on a diff, file, or PR |
| \`/hyperflow:deploy\` | Pre-push gates + commit + release + push |
| \`/hyperflow:cache\` | Read or curate \`.hyperflow/memory/\` |"
fi

CONTENT="<!-- hyperflow-runtime: $TOOL_NAME -->
# Hyperflow v$HYPERFLOW_VERSION

$INVOKE_LINE

## Canonical chain

Start from any skill — chain-starters auto-advance forward through the chain (with one Step-0 question: auto or manual).

\`scaffold\` → \`spec\` → \`scope\` → \`dispatch\` → \`audit\` → \`deploy\`

$DIRECT_ENTRIES

## Big-task workflows

Route big tasks to \`/hyperflow:workflow\`: \`flow=deep\`, \`flow=scientific\`, \`scope=system-wide\`, large migrations, repo-wide audits, high-confidence verification, or prompts that say \`run a workflow\` / \`dynamic workflow\` / \`big task\`. In Claude Code v2.1.154 or later, the workflow skill asks the native dynamic workflow runtime to create a background workflow with Hyperflow research, parallel implementation or investigation, adversarial verification, quality gates, and final synthesis. Do not set \`/effort ultracode\` or \`xhigh\` automatically; the user may enable \`/effort ultracode\` manually for session-wide automatic workflow selection.

In Codex and OpenCode, \`/hyperflow:workflow\` runs the portable workflow adapter instead of native dynamic workflows. It keeps the same phases, uses provider subagents/tasks when exposed, falls back to inline worker/reviewer phases, runs quality gates, and commits each accepted unit separately. If the active runtime cannot preserve those phases, use the normal \`scope → dispatch\` path instead.

Shared doctrine (autonomy rules, model routing, output style, security) lives in [skills/hyperflow/DOCTRINE.md](skills/hyperflow/DOCTRINE.md) and is referenced by each skill when invoked."

if [ -n "$HF_DIR" ]; then
  # Resolve active mode (default | lean | thorough) via the central helper.
  # Used to decide whether to collapse session-start output into one summary line.
  project_root="$(dirname "$HF_DIR")"
  HF_MODE="default"
  if [ -x "$PLUGIN_ROOT/scripts/resolve-mode.py" ]; then
    HF_MODE="$(python3 "$PLUGIN_ROOT/scripts/resolve-mode.py" "$project_root" 2>>"$HF_DIR/.session-start.log" || echo "default")"
  fi

  # Post-compaction recovery — surface the snapshot the pre-compact hook wrote
  # just before the conversation was compacted, then consume it (once only).
  if [ -f "$HF_DIR/.precompact.md" ] && [ -n "$(find "$HF_DIR/.precompact.md" -mmin -60 2>/dev/null || true)" ]; then
    CONTENT="$CONTENT

$(cat "$HF_DIR/.precompact.md" 2>/dev/null || true)"
  fi
  rm -f "$HF_DIR/.precompact.md" 2>/dev/null || true

  # Auto-archive stale artefacts and promote learnings to memory (daily-gated,
  # non-blocking; controlled by cleanup.* in ~/.hyperflow/config.json).
  if [ -x "$PLUGIN_ROOT/scripts/archive-artefacts.py" ]; then
    python3 "$PLUGIN_ROOT/scripts/archive-artefacts.py" "$HF_DIR" 2>>"$HF_DIR/.session-start.log" || true
  fi

  # D3: Session-cached context — generate .hyperflow/memory/session-context.md
  # Skip if .hyperflow/memory/ doesn't exist (scaffold hasn't run yet).
  # Non-blocking: errors go to stderr only; never fail the session.
  if [ -d "$HF_DIR/memory" ]; then
    {
      for section in "Profile:profile.md" "Architecture:architecture.md" "Conventions:conventions.md"; do
        label="${section%%:*}"
        file="${section##*:}"
        printf '## %s\n' "$label"
        if [ -f "$HF_DIR/$file" ]; then
          # Cap each section at 500 lines to bound session-context.md size and
          # avoid blocking session-start on pathological multi-thousand-line sources.
          head -n 500 "$HF_DIR/$file"
          lines=$(wc -l < "$HF_DIR/$file")
          if [ "$lines" -gt 500 ]; then
            printf '<!-- truncated: %s has %s lines, showing first 500 -->\n' "$file" "$lines"
          fi
        else
          printf '<!-- .hyperflow/%s not found — run /hyperflow:scaffold to populate -->\n' "$file"
          printf 'WARNING: session-context: .hyperflow/%s missing\n' "$file" >&2
        fi
        printf '\n'
      done
    } > "$HF_DIR/memory/session-context.md" 2>>"$HF_DIR/.session-start.log" || \
      printf 'WARNING: session-context: failed to write session-context.md\n' >&2
  fi

  # Project Snapshot (first 20 lines of each profile file)
  snap=""
  for f in profile.md architecture.md conventions.md; do
    [ -f "$HF_DIR/$f" ] && snap="${snap}### $f
$(head -20 "$HF_DIR/$f")
"
  done
  [ -n "$snap" ] && CONTENT="$CONTENT

## Project Snapshot
$snap"

  # Memory Index
  [ -f "$HF_DIR/memory/index.md" ] && CONTENT="$CONTENT

## Project Memory Index
$(head -200 "$HF_DIR/memory/index.md")"

  # Memory Compaction Advisory (non-blocking, no LLM)
  if [ -f "$HF_DIR/memory/.checksums" ]; then
    advisory="$(python3 - "$HF_DIR/memory/.checksums" "$HOME/.hyperflow/config.json" 2>>"$HF_DIR/.session-start.log" <<'PYEOF' || true
import json, os, sys
checksums_path, config_path = sys.argv[1], sys.argv[2]
threshold = 300
try:
    with open(config_path) as f:
        cfg = json.load(f)
    threshold = int(cfg.get("memory", {}).get("compactionThreshold", 300))
except (OSError, ValueError, TypeError):
    pass
if threshold < 50:
    threshold = 300
try:
    with open(checksums_path) as f:
        checksums = json.load(f)
except (OSError, ValueError):
    sys.exit(0)
if not isinstance(checksums, dict) or len(checksums) > 10000:
    sys.exit(0)
over = [(path, meta["lineCount"]) for path, meta in checksums.items()
        if isinstance(meta, dict) and isinstance(meta.get("lineCount"), int)
        and meta["lineCount"] >= threshold]
if over:
    names = ", ".join(f"{os.path.basename(p)} ({lc} lines)" for p, lc in over)
    print(f"- {names} — at or above {threshold}, run `/hyperflow:cache compact` when convenient")
PYEOF
)"
    if [ -n "$advisory" ]; then
      CONTENT="$CONTENT

## Memory Compaction Advisory
$advisory"
    fi
  fi

  # Auto-bridge (non-blocking, no LLM)
  # Writes ./CLAUDE.md doctrine block when missing/outdated so Desktop / web /
  # IDE surfaces (which don't load the CLI plugin) honor hyperflow's portable
  # subset. Mode (auto / manual / off) lives at .hyperflow/.bridge-mode.
  if [ -d "$HF_DIR" ] && [ -x "$PLUGIN_ROOT/scripts/auto-bridge.py" ]; then
    project_root="$(dirname "$HF_DIR")"
    bridge_line="$(python3 "$PLUGIN_ROOT/scripts/auto-bridge.py" "$PLUGIN_ROOT" "$project_root" 2>>"$HF_DIR/.session-start.log" || true)"
    if [ -n "$bridge_line" ]; then
      CONTENT="$CONTENT

## CLAUDE.md auto-bridge
$bridge_line"
    fi
  fi

  # Auto-routing status (non-blocking, no LLM)
  # Default is `auto` (intent-detection) when .sticky is absent.
  sticky_line="$(python3 - "$HF_DIR/.sticky" <<'PYEOF' 2>>"$HF_DIR/.session-start.log" || true
import os, sys
path = sys.argv[1]
state, since, trigger = "auto", "", "default"
if os.path.exists(path):
    try:
        with open(path) as f:
            for line in f:
                line = line.strip()
                if line.startswith("state:"):
                    state = line.split(":", 1)[1].strip()
                elif line.startswith("since:"):
                    since = line.split(":", 1)[1].strip()
                elif line.startswith("trigger:"):
                    trigger = line.split(":", 1)[1].strip()
    except OSError:
        sys.exit(0)
labels = {
    "on":   "Auto-routing: ON (full sticky) — every task-shaped message routes through hyperflow.",
    "auto": "Auto-routing: AUTO (default) — messages containing chain-starter verbs (audit, debug, fix, brainstorm, workflow, scope, deploy, review, …) auto-route.",
    "off":  "Auto-routing: OFF — only explicit /hyperflow:* slash commands route. Re-enable with /hyperflow:sticky auto.",
}
line_out = labels.get(state)
if line_out:
    suffix = []
    if since:
        suffix.append(f"since {since[:16].replace('T', ' ')}")
    if trigger and trigger != "default":
        suffix.append(f"trigger: {trigger}")
    if suffix:
        line_out += " · " + " · ".join(suffix)
    print(line_out)
PYEOF
)"
  if [ -n "$sticky_line" ]; then
    CONTENT="$CONTENT

## Auto-routing status
$sticky_line"
  fi

  # Active Tasks
  if [ -d "$HF_DIR/tasks" ]; then
    tlist=""
    for tf in "$HF_DIR/tasks/"*.md; do
      [ -f "$tf" ] && tlist="${tlist}- $(basename "$tf")
"
    done
    [ -n "$tlist" ] && CONTENT="$CONTENT

## Active Tasks (incomplete from prior sessions)
$tlist"
  fi

  # Lean-mode situational summary (one line consolidating all state).
  # Emitted ONLY when mode=lean. Dedicated ## sections above continue to fire
  # when they have attention-needed content (memory compaction advisory, sticky
  # upgrade announcement, auto-bridge wrote/refreshed) — the summary is the
  # always-present situational floor, not a replacement for the action signals.
  if [ "$HF_MODE" = "lean" ] && [ -x "$PLUGIN_ROOT/scripts/lean-summary.py" ]; then
    lean_line="$(python3 "$PLUGIN_ROOT/scripts/lean-summary.py" "$PLUGIN_ROOT" "$project_root" 2>>"$HF_DIR/.session-start.log" || true)"
    if [ -n "$lean_line" ]; then
      CONTENT="$CONTENT

## Hyperflow status (lean mode)
$lean_line"
    fi
  fi
fi

# ── Update check (non-blocking · cached 24h · never fails the session) ─────────
# Compares the installed VERSION against the latest GitHub release tag. When a
# newer version exists, injects a self-instructing notice telling the orchestrator
# to ask the user (AskUserQuestion) whether to update now.
if [ "$HYPERFLOW_VERSION" != "unknown" ]; then
  cache_dir="${HOME:-/tmp}/.hyperflow"
  cache_file="$cache_dir/.update-check"
  latest=""
  if [ -f "$cache_file" ] && [ -n "$(find "$cache_file" -mmin -1440 2>/dev/null || true)" ]; then
    latest="$(cat "$cache_file" 2>/dev/null || true)"
  else
    TIMEOUT=""
    command -v timeout >/dev/null 2>&1 && TIMEOUT="timeout 4"
    latest="$($TIMEOUT git ls-remote --tags --refs --sort=-v:refname \
        https://github.com/Mohammed-Abdelhady/hyperflow.git 'v*' 2>/dev/null \
        | head -1 | sed -E 's@.*/v@@' || true)"
    if [ -n "$latest" ]; then
      mkdir -p "$cache_dir" 2>/dev/null || true
      printf '%s' "$latest" > "$cache_file" 2>/dev/null || true
    fi
  fi
  if [ -n "$latest" ] && [ "$latest" != "$HYPERFLOW_VERSION" ]; then
    newer="$(python3 -c 'import sys
def t(v): return tuple(int(x) for x in v.split("-")[0].split(".")[:3])
try: print("yes" if t(sys.argv[2]) > t(sys.argv[1]) else "no")
except Exception: print("no")' "$HYPERFLOW_VERSION" "$latest" 2>/dev/null || echo no)"
    if [ "$newer" = "yes" ]; then
      if git -C "$PLUGIN_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
        update_cmd="git -C \"$PLUGIN_ROOT\" pull --ff-only"
      else
        update_cmd="claude plugin update hyperflow@hyperflow-marketplace"
      fi
      CONTENT="$CONTENT

## Hyperflow update available
<!-- hyperflow-update current=$HYPERFLOW_VERSION latest=$latest -->
A newer Hyperflow is available — **v$HYPERFLOW_VERSION → v$latest**. Before acting on the user's request this session, tell them an update is available and use **AskUserQuestion** to ask whether to update now (options: \`Update now\` / \`Skip\`). On \`Update now\`, run \`$update_cmd\`, confirm it succeeded, then continue with their request on the new version. On \`Skip\`, proceed on the current version. Ask at most once per session."
    fi
  fi
fi

ESCAPED=$(printf '%s' "$CONTENT" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')
cat <<JSONEOF
{
  "type": "system-prompt-inject",
  "content": $ESCAPED
}
JSONEOF
