koh3.plugin.zsh

6.4 KB · raw

1# koh3.plugin.zsh — Koh 3 in your zsh prompt, plus tab completion.2#3#   kiln · v2.1.0 · 6 saves · 4.2 MB !4#   →5#6# The segment reads `koh status --json`, never the .koh file itself, so it7# works with any schema and needs no sqlite3. `!` means unsaved changes and8# `⚑` a flagged head save. Koh 2 repos (a .koh folder) are left to Koh 2's9# plugin.10#11# Install: source this file from ~/.zshrc. To keep your own prompt, set12# KOH_DEFAULT_PROMPT=false first and put $(koh_prompt) where you want it.13# koh_prompt_info gives the same text without colour, for Starship or p10k:14#15#   [custom.koh]16#   command = "koh_prompt_info"17#   when = "test -f .koh"18#   shell = ["zsh"]1920: ${KOH_COLOR_DIR:=73}21: ${KOH_COLOR_VERSION:=214}22: ${KOH_COLOR_META:=245}23: ${KOH_COLOR_ALERT:=203}24: ${KOH_SHOW_VERSION:=true}25: ${KOH_SHOW_SAVES:=true}26: ${KOH_SHOW_SIZE:=true}27: ${KOH_PROMPT_CHAR:=→}28: ${KOH_DEFAULT_PROMPT:=true}2930# During the transition the Koh 3 binary is koh3; after the cut-over it is koh.31_koh_bin() {32  if [[ -n $KOH_BIN ]]; then print -r -- $KOH_BIN33  elif (( $+commands[koh3] )); then print koh334  else print koh35  fi36}3738# The folder holding a Koh 3 .koh file, searching upward like koh does.39_koh_root() {40  local dir=$PWD41  while true; do42    [[ -f $dir/.koh ]] && { print -r -- $dir; return 0; }43    [[ -d $dir/.koh || $dir == / ]] && return 144    dir=${dir:h}45  done46}4748# The project's own version, from whichever manifest it keeps.49_koh_version() {50  local root=$1 line51  if [[ -f $root/VERSION ]]; then52    read -r line <$root/VERSION && print -r -- ${line// /}53  elif [[ -f $root/v.mod ]]; then54    [[ "$(<$root/v.mod)" =~ "version: *'([^']+)'" ]] && print -r -- $match[1]55  elif [[ -f $root/package.json ]]; then56    [[ "$(<$root/package.json)" =~ '"version" *: *"([^"]+)"' ]] && print -r -- $match[1]57  elif [[ -f $root/Cargo.toml ]]; then58    [[ "$(<$root/Cargo.toml)" =~ $'(^|\n)version *= *"([^"]+)"' ]] && print -r -- $match[2]59  elif [[ -f $root/build.zig.zon ]]; then60    [[ "$(<$root/build.zig.zon)" =~ '\.version *= *"([^"]+)"' ]] && print -r -- $match[1]61  elif [[ -f $root/pyproject.toml ]]; then62    [[ "$(<$root/pyproject.toml)" =~ $'(^|\n)version *= *"([^"]+)"' ]] && print -r -- $match[2]63  fi64}6566_koh_size() {67  local -F value=$168  local unit=B69  for next in KB MB GB; do70    (( value < 1024 )) && break71    (( value /= 1024 ))72    unit=$next73  done74  [[ $unit == B ]] && print -r -- "$1 B" || printf '%.1f %s' $value $unit75}7677# _koh_segment <colour?> — the segment text, or nothing outside a Koh 3 repo.78_koh_segment() {79  local colour=$1 root json80  root=$(_koh_root) || return81  json=$(cd $root && command $(_koh_bin) status --json 2>/dev/null) || return82  local saves size dirty flagged version83  [[ $json =~ '"saves":([0-9]+)' ]] && saves=$match[1]84  [[ $json =~ '"size_bytes":([0-9]+)' ]] && size=$(_koh_size $match[1])85  [[ $json == *'"dirty":true'* ]] && dirty=186  [[ $json == *'"head_flagged":true'* ]] && flagged=187  [[ $KOH_SHOW_VERSION == true ]] && version=$(_koh_version $root)8889  local c_dir= c_version= c_meta= c_alert= reset=90  if [[ -n $colour ]]; then91    c_dir="%F{$KOH_COLOR_DIR}" c_version="%F{$KOH_COLOR_VERSION}"92    c_meta="%F{$KOH_COLOR_META}" c_alert="%F{$KOH_COLOR_ALERT}" reset=%f93  fi94  local out="${c_dir}${root:t}${reset}"95  [[ -n $version ]] && out+="${c_meta} · ${reset}${c_version}v${version#v}${reset}"96  [[ $KOH_SHOW_SAVES == true && -n $saves ]] && out+="${c_meta} · $saves save$( (( saves == 1 )) || print s)${reset}"97  [[ $KOH_SHOW_SIZE == true && -n $size ]] && out+="${c_meta} · $size${reset}"98  [[ -n $dirty ]] && out+=" ${c_alert}!${reset}"99  [[ -n $flagged ]] && out+=" ${c_alert}⚑${reset}"100  print -rn -- $out101}102103koh_prompt() { _koh_segment colour; }104koh_prompt_info() { _koh_segment; }105106if [[ $KOH_DEFAULT_PROMPT == true ]]; then107  setopt prompt_subst108  _koh_precmd() {109    local last=$?110    local segment=$(koh_prompt)111    if [[ -z $segment ]]; then112      segment="%F{$KOH_COLOR_DIR}%2~%f"113    fi114    local char="%F{$KOH_COLOR_META}${KOH_PROMPT_CHAR}%f"115    (( last != 0 )) && char="%F{$KOH_COLOR_ALERT}${KOH_PROMPT_CHAR}%f"116    PROMPT="${segment}"$'\n'"${char} "117  }118  autoload -Uz add-zsh-hook119  add-zsh-hook precmd _koh_precmd120fi121122# Tab completion: the commands of `koh`'s usage, and save IDs where one is taken.123_koh3() {124  local -a commands=(125    'save:snapshot the working tree'126    'checkpoint:save, even with no changes'127    'status:what changed since the last save'128    'view:the history, saves and checkpoints'129    'diff:unified diff'130    'undo:step back n saves'131    'revert:discard unsaved changes'132    'load:restore any save'133    'remote:choose S3 or a folder, once per machine'134    'offer:send new saves to the remote'135    'steal:bring in saves from the remote'136    'flag:mark a save unsafe'137    'pluck:apply one save, or its inverse'138    'peek:restore a save into a temp folder'139    'init:create .koh here'140    'doctor:integrity check and cleanup'141    'strip:remove a path from history'142    'migrate:convert a Koh 2 repo here'143    'man:the manual, built in'144  )145  _koh3_ids() {146    local -a ids147    ids=(${(f)"$(command $(_koh_bin) view --json 2>/dev/null |148      grep -o '"id":"[0-9a-f]*","parent":"[0-9a-f]*","message":"[^"]*"' |149      sed -E 's/"id":"([0-9a-f]*)".*"message":"([^"]*)"/\1:\2/' | head -30)"})150    _describe 'save' ids151  }152  if (( CURRENT == 2 )); then153    _describe 'koh command' commands154    return155  fi156  case $words[2] in157    save|checkpoint) _arguments '--agent[model name]:agent:' '--session[session name]:session:' '--json' ;;158    view)            _arguments '--checkpoints[start on the checkpoints tab]' '--since[filter by time]:expr:' '--session[session name]:session:' '--json' ;;159    diff|load|peek)  _koh3_ids ;;160    pluck)           _arguments '--reverse' '--dry-run' '*:save:_koh3_ids' ;;161    flag)            _arguments '--reason[why]:reason:' '--clear' '--json' '*:save:_koh3_ids' ;;162    undo)            _arguments '--session' '--yes' ;;163    revert)          _arguments '--yes' ;;164    remote)          _arguments '1:kind:(folder s3)' '--forget' '--json' ;;165    steal)           _arguments '--list' '--mine' '--theirs' '--json' ;;166    strip)           _arguments '--save[one save]:save:_koh3_ids' '*:path:_files' ;;167    doctor)          _arguments '--backup[copy .koh to]:path:_files' ;;168    migrate)         _arguments '--lane[Koh 2 lane]:lane:(dev main)' ;;169    man)             _describe 'command' commands ;;170  esac171}172(( $+functions[compdef] )) && compdef _koh3 ${$(_koh_bin):t}