> ## Documentation Index
> Fetch the complete documentation index at: https://docs.antasphere.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The commands

> This page lists the commands you type during the hackathon, taken from the CLI's own help. The complete reference, written for agents, is the CLI reference.

export const HeroBand = ({eyebrow, title, lede, drawing = 'latitudes', seed = 976086463, level = 0.55, compact = false, fieldSeed = 1872629950}) => {
  const box = useRef(null);
  const groundRef = useRef(null);
  const bodyRef = useRef(null);
  const canvasRef = useRef(null);
  useEffect(() => {
    const boxEl = box.current;
    const groundEl = groundRef.current;
    const bodyEl = bodyRef.current;
    const canvas = canvasRef.current;
    if (!boxEl || !groundEl || !bodyEl || !canvas) return;
    const TAU = Math.PI * 2;
    const DPR = Math.min(window.devicePixelRatio || 1, 2);
    const isDark = () => document.documentElement.classList.contains('dark');
    const PALETTES = {
      'umber-field': {
        light: true,
        base: '#ECE4D6',
        hues: ['#FAF6EE', '#F6F0E4', '#ECE4D6', '#D9C6AC', '#C9B193', '#F8F3EA', '#B89E80']
      },
      'umber-field-dark': {
        light: false,
        base: '#2F2B27',
        hues: ['#2B2722', '#34302C', '#3E3A35', '#4C4743', '#5A5651', '#302C28', '#6A6560']
      }
    };
    const LOW_W = 116;
    function mulberry32(s) {
      let a = s >>> 0;
      return function () {
        a |= 0;
        a = a + 0x6d2b79f5 | 0;
        let t = Math.imul(a ^ a >>> 15, 1 | a);
        t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
        return ((t ^ t >>> 14) >>> 0) / 4294967296;
      };
    }
    function buildBlobs(s, palName) {
      const pal = PALETTES[palName];
      const rnd = mulberry32(s);
      const count = 7 + Math.floor(rnd() * 4);
      const blobs = [];
      for (let i = 0; i < count; i++) {
        blobs.push({
          color: pal.hues[Math.floor(rnd() * pal.hues.length)],
          x: rnd(),
          y: rnd(),
          r: 0.22 + rnd() * 0.4,
          alpha: 0.75 + rnd() * 0.25,
          px: 0.5 + rnd() * 1.5,
          py: 0.5 + rnd() * 1.5,
          ax: 0.02 + rnd() * 0.05,
          ay: 0.02 + rnd() * 0.05,
          ph: rnd() * TAU
        });
      }
      return blobs;
    }
    function renderLow(blobs, palName, W, H) {
      const pal = PALETTES[palName];
      const lw = LOW_W;
      const lh = Math.max(8, Math.round(LOW_W * H / W));
      const low = document.createElement('canvas');
      low.width = lw;
      low.height = lh;
      const c = low.getContext('2d');
      c.fillStyle = pal.base;
      c.fillRect(0, 0, lw, lh);
      blobs.forEach(b => {
        const x = (b.x + Math.sin(b.ph) * b.ax) * lw;
        const y = (b.y + Math.cos(b.ph) * b.ay) * lh;
        const r = b.r * lw;
        const g = c.createRadialGradient(x, y, 0, x, y, r);
        g.addColorStop(0, b.color);
        g.addColorStop(1, b.color.slice(0, 7) + '00');
        c.fillStyle = g;
        c.globalAlpha = b.alpha;
        c.beginPath();
        c.arc(x, y, r, 0, TAU);
        c.fill();
        c.globalAlpha = 1;
      });
      return low;
    }
    function noiseTile(size, amp) {
      const c = document.createElement('canvas');
      c.width = c.height = size;
      const ctx = c.getContext('2d');
      const img = ctx.createImageData(size, size);
      const d = img.data;
      for (let i = 0; i < d.length; i += 4) {
        const v = 128 + (Math.random() - 0.5) * 255 * amp;
        d[i] = d[i + 1] = d[i + 2] = v;
        d[i + 3] = 255;
      }
      ctx.putImageData(img, 0, 0);
      return c;
    }
    const OVER = 0.24;
    let sheet = null;
    let sheetKey = '';
    function makeSheet(W, H) {
      const key = isDark() ? 'umber-field-dark' : 'umber-field';
      const sw = Math.round(W * (1 + OVER * 2));
      const sh = Math.round(H * (1 + OVER * 2));
      const c = document.createElement('canvas');
      c.width = sw;
      c.height = sh;
      const ctx = c.getContext('2d');
      const low = renderLow(buildBlobs(fieldSeed, key), key, sw, sh);
      ctx.imageSmoothingEnabled = true;
      ctx.imageSmoothingQuality = 'high';
      ctx.filter = 'blur(' + Math.max(4, Math.round(sw * 0.018)) + 'px)';
      ctx.drawImage(low, -sw * 0.06, -sh * 0.06, sw * 1.12, sh * 1.12);
      ctx.filter = 'none';
      sheet = c;
      sheetKey = key + ':' + sw + 'x' + sh;
    }
    let slide = {
      x: 0,
      y: 0
    };
    function paintGround() {
      const W = Math.round(groundEl.offsetWidth * DPR);
      const H = Math.round(groundEl.offsetHeight * DPR);
      if (W < 2 || H < 2) return;
      if (groundEl.width !== W || groundEl.height !== H) {
        groundEl.width = W;
        groundEl.height = H;
      }
      const key = isDark() ? 'umber-field-dark' : 'umber-field';
      const sw = Math.round(W * (1 + OVER * 2));
      const sh = Math.round(H * (1 + OVER * 2));
      if (!sheet || sheetKey !== key + ':' + sw + 'x' + sh) makeSheet(W, H);
      const ctx = groundEl.getContext('2d');
      const dx = Math.max(-W * OVER, Math.min(W * OVER, slide.x * DPR));
      const dy = Math.max(-H * OVER, Math.min(H * OVER, slide.y * DPR));
      ctx.clearRect(0, 0, W, H);
      ctx.drawImage(sheet, -W * OVER + dx, -H * OVER + dy);
    }
    function turn(p, pitch, yaw) {
      const ct = Math.cos(pitch);
      const st = Math.sin(pitch);
      const cp = Math.cos(yaw);
      const sp = Math.sin(yaw);
      const y2 = p.y * ct - p.z * st;
      const z2 = p.y * st + p.z * ct;
      return {
        x: p.x * cp + z2 * sp,
        y: y2,
        z: -p.x * sp + z2 * cp
      };
    }
    function ringAround(axis, h, samples) {
      const r = Math.sqrt(Math.max(0, 1 - h * h));
      const ref = Math.abs(axis.y) < 0.9 ? {
        x: 0,
        y: 1,
        z: 0
      } : {
        x: 1,
        y: 0,
        z: 0
      };
      let ux = axis.y * ref.z - axis.z * ref.y;
      let uy = axis.z * ref.x - axis.x * ref.z;
      let uz = axis.x * ref.y - axis.y * ref.x;
      const ul = Math.hypot(ux, uy, uz);
      ux /= ul;
      uy /= ul;
      uz /= ul;
      const vx = axis.y * uz - axis.z * uy;
      const vy = axis.z * ux - axis.x * uz;
      const vz = axis.x * uy - axis.y * ux;
      const out = [];
      for (let k = 0; k <= samples; k++) {
        const th = k / samples * TAU;
        const c = Math.cos(th) * r;
        const s = Math.sin(th) * r;
        out.push({
          x: axis.x * h + c * ux + s * vx,
          y: axis.y * h + c * uy + s * vy,
          z: axis.z * h + c * uz + s * vz
        });
      }
      return out;
    }
    function strokeRuns(f, v, pts, near, far) {
      const {ctx} = f;
      let open = false;
      let side = false;
      const close = () => {
        if (!open) return;
        const a = side ? near : far;
        if (a > 0) {
          ctx.strokeStyle = f.ink(a);
          ctx.stroke();
        }
        open = false;
      };
      for (const p of pts) {
        const sx = v.cx + p.x * v.R;
        const sy = v.cy - p.y * v.R;
        const front = p.z >= 0;
        if (open && front !== side) {
          ctx.lineTo(sx, sy);
          close();
        }
        if (!open) {
          ctx.beginPath();
          ctx.moveTo(sx, sy);
          open = true;
          side = front;
        } else {
          ctx.lineTo(sx, sy);
        }
      }
      close();
    }
    function outline(f, v, a, w = 0.8) {
      f.ctx.lineWidth = Math.max(1, f.dpr * w);
      f.ctx.strokeStyle = f.ink(a);
      f.ctx.beginPath();
      f.ctx.arc(v.cx, v.cy, v.R, 0, TAU);
      f.ctx.stroke();
    }
    function view(f, scale = 0.42) {
      return {
        cx: f.W / 2,
        cy: f.H / 2,
        R: Math.min(f.W, f.H) * scale
      };
    }
    const up = {
      x: 0,
      y: 1,
      z: 0
    };
    function latitudes(s) {
      const squash = 0.26 + mulberry32(s)() * 0.12;
      const lean = Math.asin(squash);
      const N = 13;
      const rows = Array.from({
        length: N - 1
      }, (_, i) => (i + 1) / N * 2 - 1);
      const rings = rows.map(h => ringAround(up, h, 72));
      return f => {
        const v = view(f);
        outline(f, v, 0.4);
        f.ctx.lineWidth = Math.max(1, f.dpr * 0.8);
        const pitch = lean + f.pitch;
        rings.forEach((ring, i) => {
          const h = Math.abs(rows[i]);
          strokeRuns(f, v, ring.map(p => turn(p, pitch, f.yaw)), 0.2 + h * 0.12, 0.05 + h * 0.03);
        });
        let pole = turn(up, pitch, f.yaw);
        if (pole.z < 0) pole = {
          x: -pole.x,
          y: -pole.y,
          z: -pole.z
        };
        f.ctx.fillStyle = f.ink(0.5);
        f.ctx.beginPath();
        f.ctx.arc(v.cx + pole.x * v.R, v.cy - pole.y * v.R, Math.max(1.5, f.dpr * 1.5), 0, TAU);
        f.ctx.fill();
      };
    }
    function meridians(s) {
      const rnd = mulberry32(s);
      const lean = 0.3 + rnd() * 0.14;
      const greats = [];
      for (let k = 0; k < 9; k++) {
        const th = k / 9 * Math.PI;
        greats.push(ringAround({
          x: Math.cos(th),
          y: 0,
          z: Math.sin(th)
        }, 0, 96));
      }
      const rows = [-0.75, -0.45, -0.15, 0.15, 0.45, 0.75];
      const parallels = rows.map(h => ringAround(up, h, 72));
      return f => {
        const v = view(f);
        outline(f, v, 0.4);
        f.ctx.lineWidth = Math.max(1, f.dpr * 0.8);
        const pitch = lean + f.pitch;
        const yaw = f.yaw + f.t * 0.05;
        greats.forEach(ring => strokeRuns(f, v, ring.map(p => turn(p, pitch, yaw)), 0.3, 0.07));
        parallels.forEach(ring => strokeRuns(f, v, ring.map(p => turn(p, pitch, yaw)), 0.17, 0.045));
      };
    }
    const GOLDEN = Math.PI * (3 - Math.sqrt(5));
    const clamp01 = v => v < 0 ? 0 : v > 1 ? 1 : v;
    function spinY(p, a) {
      const c = Math.cos(a);
      const s = Math.sin(a);
      return {
        x: p.x * c + p.z * s,
        y: p.y,
        z: -p.x * s + p.z * c
      };
    }
    function plm(l, m, x) {
      let pmm = 1;
      if (m > 0) {
        const s = Math.sqrt((1 - x) * (1 + x));
        let fact = 1;
        for (let i = 1; i <= m; i++) {
          pmm *= -fact * s;
          fact += 2;
        }
      }
      if (l === m) return pmm;
      let pmmp1 = x * (2 * m + 1) * pmm;
      if (l === m + 1) return pmmp1;
      let pll = 0;
      for (let ll = m + 2; ll <= l; ll++) {
        pll = (x * (2 * ll - 1) * pmmp1 - (ll + m - 1) * pmm) / (ll - m);
        pmm = pmmp1;
        pmmp1 = pll;
      }
      return pll;
    }
    function harmonic(s) {
      const rnd = mulberry32(s);
      const pairs = [[2, 1], [3, 2], [4, 2], [5, 3], [3, 1], [4, 3], [6, 4]];
      const [l, m] = pairs[Math.floor(rnd() * pairs.length)];
      const lean = 0.28 + rnd() * 0.5;
      const spin0 = rnd() * TAU;
      const circles = [];
      let prev = plm(l, m, -0.999);
      for (let i = 1; i <= 2000; i++) {
        const x = -0.999 + 1.998 * i / 2000;
        const cur = plm(l, m, x);
        if (prev * cur < 0) circles.push(ringAround(up, x - 0.0005, 72));
        prev = cur;
      }
      const greats = Array.from({
        length: m
      }, (_, k) => {
        const phi = (Math.PI / 2 + k * Math.PI) / m;
        return ringAround({
          x: -Math.sin(phi),
          y: 0,
          z: Math.cos(phi)
        }, 0, 96);
      });
      return f => {
        const v = view(f, 0.44);
        outline(f, v, 0.35, 1);
        const spin = spin0 + f.t * 0.05;
        const pitch = lean + f.pitch;
        const place = p => turn(spinY(p, spin), pitch, f.yaw);
        f.ctx.lineWidth = Math.max(1, f.dpr * 0.85);
        for (const c of circles) strokeRuns(f, v, c.map(place), 0.46, 0.1);
        for (const g of greats) strokeRuns(f, v, g.map(place), 0.46, 0.1);
      };
    }
    function lattice(s) {
      const rnd = mulberry32(s);
      const tilt = 0.25 + rnd() * 0.5;
      const spin0 = rnd() * TAU;
      let built = null;
      const build = n => {
        const pts = [];
        for (let k = 0; k < n; k++) {
          const z = 1 - (2 * k + 1) / n;
          const r = Math.sqrt(Math.max(0, 1 - z * z));
          pts.push({
            x: r * Math.cos(k * GOLDEN),
            y: r * Math.sin(k * GOLDEN),
            z
          });
        }
        const edges = [];
        const seen = new Set();
        for (let k = 0; k < n; k++) {
          const near = pts.map((b, j) => ({
            j,
            d: (pts[k].x - b.x) ** 2 + (pts[k].y - b.y) ** 2 + (pts[k].z - b.z) ** 2
          })).filter(o => o.j !== k).sort((p, q) => p.d - q.d).slice(0, 3);
          for (const e of near) {
            const key = Math.min(k, e.j) + ':' + Math.max(k, e.j);
            if (seen.has(key)) continue;
            seen.add(key);
            edges.push([k, e.j]);
          }
        }
        return {
          n,
          pts,
          edges
        };
      };
      return f => {
        const v = view(f);
        const n = Math.min(f.W, f.H) < 560 ? 140 : 260;
        if (!built || built.n !== n) built = build(n);
        const proj = built.pts.map(p => turn(p, tilt + f.pitch, spin0 + f.t * 0.04 + f.yaw));
        const {ctx} = f;
        ctx.lineWidth = Math.max(1, f.dpr * 0.6);
        for (const [i, j] of built.edges) {
          const a = proj[i];
          const b = proj[j];
          const depth = (a.z + b.z) / 2;
          if (depth < -0.12) continue;
          ctx.strokeStyle = f.ink(0.05 + 0.2 * clamp01((depth + 1) / 2));
          ctx.beginPath();
          ctx.moveTo(v.cx + a.x * v.R, v.cy - a.y * v.R);
          ctx.lineTo(v.cx + b.x * v.R, v.cy - b.y * v.R);
          ctx.stroke();
        }
        for (const p of proj) {
          const d = clamp01((p.z + 1) / 2);
          ctx.fillStyle = f.ink(0.14 + 0.6 * d * d);
          ctx.beginPath();
          ctx.arc(v.cx + p.x * v.R, v.cy - p.y * v.R, Math.max(0.8, f.dpr * (0.5 + 1.5 * d)), 0, TAU);
          ctx.fill();
        }
        outline(f, v, 0.14);
      };
    }
    const BUILDERS = {
      latitudes,
      meridians,
      harmonic,
      lattice
    };
    const solid = (BUILDERS[drawing] || latitudes)(seed);
    let held = {
      a: 0,
      b: 0,
      t: 0
    };
    function paintBody() {
      const W = Math.round(canvas.offsetWidth * DPR);
      const H = Math.round(canvas.offsetHeight * DPR);
      if (W < 2 || H < 2) return;
      if (canvas.width !== W || canvas.height !== H) {
        canvas.width = W;
        canvas.height = H;
      }
      const ctx = canvas.getContext('2d');
      ctx.clearRect(0, 0, W, H);
      const rgb = isDark() ? '247,244,236' : '28,25,21';
      const ink = a => 'rgba(' + rgb + ',' + Math.min(0.96, a).toFixed(3) + ')';
      solid({
        ctx,
        W,
        H,
        dpr: DPR,
        ink,
        pitch: held.b,
        yaw: held.a,
        t: held.t
      });
    }
    const still = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    let stop = () => {};
    if (!still) {
      const aim = {
        x: 0,
        y: 0
      };
      const onMove = e => {
        if (e.pointerType === 'touch') return;
        const r = boxEl.getBoundingClientRect();
        const clamp = v => Math.max(-1, Math.min(1, v));
        aim.x = clamp((e.clientX - (r.left + r.width / 2)) / (window.innerWidth / 2));
        aim.y = clamp((e.clientY - (r.top + r.height / 2)) / (window.innerHeight / 2));
      };
      const onLeave = () => {
        aim.x = 0;
        aim.y = 0;
      };
      window.addEventListener('pointermove', onMove, {
        passive: true
      });
      document.documentElement.addEventListener('pointerleave', onLeave);
      let seen = false;
      const io = new IntersectionObserver(([e]) => {
        seen = e.isIntersecting;
      });
      io.observe(boxEl);
      const at = {
        x: 0,
        y: 0,
        vx: 0,
        vy: 0
      };
      const t0 = performance.now();
      let last = t0;
      let rafId = requestAnimationFrame(function tick(now) {
        rafId = requestAnimationFrame(tick);
        const dt = Math.min(0.05, (now - last) / 1000);
        last = now;
        if (!seen) return;
        at.vx += ((aim.x - at.x) * 18 - at.vx * 8.5) * dt;
        at.vy += ((aim.y - at.y) * 18 - at.vy * 8.5) * dt;
        at.x += at.vx * dt;
        at.y += at.vy * dt;
        const t = (now - t0) / 1000;
        const a = at.x * 0.4 + Math.sin(t * 0.21) * 0.07;
        const away = 0.2;
        const b = at.y < 0 ? -away * Math.tanh(-at.y * 0.3 / away) : at.y * 0.3;
        held = {
          a,
          b,
          t
        };
        const radius = bodyEl.offsetWidth * 0.42;
        slide = {
          x: -a * radius,
          y: -b * radius * 0.6
        };
        paintGround();
        const sway = compact ? 0.5 : 0.8;
        const dx = (Math.sin(t * 0.43) * 6 + Math.sin(t * 0.19 + 1.3) * 4) * sway;
        const dy = (Math.cos(t * 0.37) * 8 + Math.sin(t * 0.23) * 4) * sway;
        const lift = 1 + Math.sin(t * 0.31 + 0.6) * 0.016;
        bodyEl.style.transform = 'translate3d(' + dx.toFixed(2) + 'px, ' + dy.toFixed(2) + 'px, 0) scale(' + lift.toFixed(4) + ')';
        paintBody();
      });
      stop = () => {
        cancelAnimationFrame(rafId);
        io.disconnect();
        window.removeEventListener('pointermove', onMove);
        document.documentElement.removeEventListener('pointerleave', onLeave);
      };
    }
    const grainEl = boxEl.querySelector('.ant-hero-grain');
    if (grainEl && !grainEl.style.backgroundImage) {
      grainEl.style.backgroundImage = 'url(' + noiseTile(512, 0.9).toDataURL() + ')';
      grainEl.style.backgroundSize = 512 / DPR + 'px';
    }
    const repaint = () => {
      sheet = null;
      paintGround();
      paintBody();
    };
    const ro = new ResizeObserver(repaint);
    ro.observe(boxEl);
    const mo = new MutationObserver(repaint);
    mo.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    repaint();
    return () => {
      stop();
      ro.disconnect();
      mo.disconnect();
    };
  }, [drawing, seed, compact, fieldSeed]);
  return <div className={'ant-hero' + (compact ? ' compact' : '')} data-hero ref={box} style={{
    '--level': level
  }}>
      <canvas className="ant-hero-ground" ref={groundRef} aria-hidden="true" />
      <div className="ant-hero-grain" aria-hidden="true" />
      <div className="ant-hero-shade" aria-hidden="true" />
      <div className="ant-hero-drawing" aria-hidden="true">
        <div className="ant-hero-body" ref={bodyRef}>
          <canvas ref={canvasRef} />
        </div>
      </div>
      <div className="ant-hero-words">
        {eyebrow ? <p className="ant-hero-eyebrow">{eyebrow}</p> : null}
        <h1 className="ant-hero-title">{title}</h1>
        {lede && !compact ? <p className="ant-hero-lede">{lede}</p> : null}
      </div>
    </div>;
};

<HeroBand eyebrow={"Getting started"} title={"The commands"} drawing={"meridians"} seed={523086640} compact={true} />

This page lists the commands you type during the hackathon, taken from the CLI's own help. The complete
reference, written for agents, is [the CLI reference](/hackathon/agents/cli).

## Install

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
npm i -g @antasphere/hackathon
hackathon --version
```

This installs the `hackathon` command. The second line prints the version number, so you know the install
worked.

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon completion zsh   # or bash, or fish
```

`hackathon completion` prints a completion script for bash, zsh or fish.

## Sign in

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon login
hackathon connect
```

`hackathon login` asks your Antasphere email and the code it sends, and stores that Antasphere login once for
every Antasphere tool on this machine. `hackathon connect` then checks your place on the roster. Neither
takes a URL: the command knows the hackathon lives at `https://app.hackathon.antasphere.com`. An account
whose organizations do not open the hackathon reads Antasphere's refusal and exits with code 3
([Who can sign in](/hackathon/getting-started/access)).

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon whoami   # the identity behind your API key
hackathon logout   # forget the stored key and disconnect from Antasphere
```

`hackathon whoami` shows the identity behind your API key. `hackathon logout` forgets the stored API key of
the active profile. A profile connected through the hub also disconnects from Antasphere, and its cached key
is revoked.

## Your team's app

### connect

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon connect                        # sign in and check your place on the roster
hackathon connect --github <login>       # also save your GitHub username
```

* `--github <login>` is your GitHub username. It is saved on your roster row, and it is the account the team
  repository invites.

### setup

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon setup                          # clone, install the agent files, start the app
hackathon setup --directory <path>       # put the clone somewhere else
hackathon setup --github <login>         # save your GitHub username before the clone
hackathon setup --agent codex            # install the files for Codex instead of Claude Code
hackathon setup --port <n>               # run the app on another host port
hackathon setup --no-start               # clone and install, do not build or start
hackathon setup --timeout <seconds>      # wait longer for the app to be ready
```

`hackathon setup` clones your team's repository, installs the agent files and starts the app locally.

* `--directory <path>` is where the clone goes. It must be a missing or empty directory, or the existing
  clone. The default is `~/Antasphere/hackathon/<team>`.
* `--github <login>` is your GitHub username. It is saved on your roster row before the clone, as with
  `connect --github`.
* `--agent <name>` is the agent whose file conventions are installed. It is `claude-code` or `codex`, and the
  default is `claude-code`.
* `--port <n>` is the host port of the app. The default is the template's.
* `--no-start` clones and installs the files. It does not build or start the app.
* `--timeout <seconds>` is how long to wait for the readiness probe. The default is 180.

### doctor

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon doctor                         # check everything, change nothing
hackathon doctor --directory <path>      # check a clone in another folder
hackathon doctor --port <n>              # check another host port
```

`hackathon doctor` checks the machine, the instance, the credential, the roster and the clone. It changes
nothing.

* `--directory <path>` is the clone to check (the push, the installer skill, the manifest, the app). The
  default is the folder `setup` uses.
* `--port <n>` is the host port to check instead of the template's.

## The clock

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon event status          # the phase, the deadlines, your role and team, the teams
hackathon event status --json   # the same, as JSON
```

`hackathon event status` prints the phase, the deadlines, your role and team, and the teams. The phase and the
deadlines are the server's clock. The details are in
[The clock, the grace and the freeze](/hackathon/concepts/the-clock).

## Your submission

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon submissions list              # the intake, and your team's line
hackathon submissions show <project>    # your team's submission in full
```

`hackathon submissions list` prints the intake and, for a participant, one line: your own team's, with the
state, the readiness, the last received revision and the frozen evidence (an organizer sees every team).
`hackathon submissions show` prints your team's submission in full: the repository, the receipts, the frozen
evidence and the exceptions. Another team's submission is not readable.

After a push, check the last received revision against `git rev-parse HEAD`.

## Your two decks

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon decks link --demo <url> --pitch <url>   # set your team's deck links
hackathon decks show                              # your team's three links
hackathon decks gallery                           # one line per team
```

`hackathon decks link` sets your team's demo deck, presentation and vote presentation links. Each one is a
Slideless share link.

* `--demo <url>` is the demo deck, an https Slideless share link (`/v/<token>/`).
* `--pitch <url>` is the presentation deck, an https Slideless share link (`/v/<token>/`).
* `--vote <url>` is the vote presentation, the deck the other participants watch on the Vote page and rank,
  an https Slideless share link (`/v/<token>/`).
* `--team <team>` is the team's name or id. An organizer passes it. A participant gets their own team by
  default.

`hackathon decks show` shows your team's demo deck, presentation and vote presentation links. `hackathon decks gallery` prints
one line per team, with the jury deck, the community deck and the view of each released one. How the decks are
used is in [Decks and the community vote](/hackathon/concepts/decks-and-voting).

## Chats and help requests

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon chats list                              # the channels you can read
hackathon chats read <channelId>                  # one page of posts, newest first
hackathon chats read <channelId> --limit <n>      # a page size from 1 to 100
hackathon chats read <channelId> --cursor <cursor>   # the next page
hackathon chats post <channelId> <words>          # post a message
hackathon chats post <channelId> --file <path>    # post a message read from a file
```

`hackathon chats list` shows the channels you can read. The general channel comes first, then the team chats,
then the request threads. `hackathon chats read` shows one page of a channel's posts, newest first.
`hackathon chats post` posts a plain-text message of 1 to 4000 characters in a channel you can read. Its words
are joined by one space.

* `--file <path>` reads the message from a UTF-8 file instead.
* `--limit <n>` is the page size, from 1 to 100.
* `--cursor <cursor>` resumes from a previous `nextCursor`.

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon requests list                                  # your team's requests, newest first
hackathon requests list --status open                    # only one status
hackathon requests show <requestId>                      # one request and its thread
hackathon requests open --title <text> --body <text>     # ask the advisors for help
hackathon requests resolve <requestId>                   # mark a request resolved
hackathon requests reopen <requestId>                    # open a request again
```

`hackathon requests list` shows the requests you can see, newest first. A participant sees their team's
requests. `hackathon requests show` shows one request with its team, its status, who claimed and resolved it,
its body and its thread. `hackathon requests open` asks the advisors for help for your own team.
`hackathon requests resolve` marks a request resolved. `hackathon requests reopen` opens it again and clears
its claim. A member of the team, the claiming advisor or an organizer can resolve or reopen a request.

* `--status <status>` shows only one status, `open`, `claimed` or `resolved`.
* `--limit <n>` is the page size, from 1 to 100.
* `--cursor <cursor>` resumes from a previous `nextCursor`.
* `--title <text>` is one line of 1 to 200 characters.
* `--body <text>` is what you need, in 1 to 4000 characters.

### For coaches

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon requests claim <requestId>     # take a request
hackathon requests unclaim <requestId>   # let a claimed request go
```

The CLI calls coaches advisors. An advisor takes a request with `hackathon requests claim`. The claiming
advisor or an organizer lets a claimed request go with `hackathon requests unclaim`. An advisor also sees
every team's requests in `hackathon requests list`.

## The feed

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon feed show                  # the pinned posts, newest pin first
hackathon feed list                  # one page of posts, newest first
hackathon feed post <words>          # post a message
hackathon feed post --file <path>    # post a message read from a file
```

The feed is the event's general channel. `hackathon feed show` shows the channel and its pinned posts, newest
pin first. `hackathon feed list` shows one page of posts, newest first, and takes `--limit` and `--cursor`
like `chats read`. `hackathon feed post` posts a plain-text message of 1 to 4000 characters. `--file <path>`
reads the message from a UTF-8 file instead.

## The vote

The Vote section is off unless the organizers turn it on.

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon community status                             # the vote state, your standing, the candidates
hackathon community ballot show                        # the candidates, your draft, your submission
hackathon community ballot save --ranking <ids>        # keep a draft while voting is open
hackathon community ballot submit --ranking <ids>      # submit a complete ranking
```

`hackathon community status` shows the vote state, the window, your standing and ballot line, the candidates
and the released results. `hackathon community ballot show` shows the candidates you may rank, your draft and
your submission. `hackathon community ballot save` keeps a draft while voting is open, and the draft may be
partial. `hackathon community ballot submit` submits a complete ranking of every candidate. It replaces your
previous submission.

* `--ranking <ids>` is the project ids, comma-separated, best first.
* `--eligibility-version <n>` is the eligibility version you read. By default the CLI reads it first.
* `--expected-revision <n>` is the ballot revision you read, 0 for none. By default the CLI reads it first.

## Machine output and exit codes

Add `--json` to any command for machine-readable JSON output.

| Code | Class | When |
| - | - | - |
| 0 | ok | Done. |
| 1 | error | An unexpected failure (a server 500, a bug). |
| 2 | usage | A flag missing or wrong. |
| 3 | access | No credential, an expired or refused one, a refusal by Antasphere (no organization of yours opens the hackathon), an account not on the roster, no event yet, no team yet, no repository assigned yet, no push access on GitHub, GitHub refused the clone or the push. |
| 4 | environment | Git, its identity, Node.js, Docker, the daemon or Compose missing or failing; less than 2 GiB free; the port taken; the installer digest not matching; the manifest invalid; the app not ready in time; the push probe failing for another reason than access. |
| 5 | conflict | The directory is not empty and is not the assigned clone. |

## Which workspace

A key belongs to a person, and a person can belong to several organizations. `hackathon workspaces` lists
yours and marks the one the commands run in with `*`. Nothing is saved on your machine: a command runs in
your Antasphere default organization, and `--org <name or id>` names another one for that command only.
The organizers' event is the organization they added you to, so for a participant the default is the
right one.

```bash theme={"theme":{"light":"rose-pine-dawn","dark":"rose-pine-moon"}}
hackathon workspaces                            # list your organizations
hackathon event status --org "Malt × Antasphere"  # one command in another organization
```
