loki-mode 7.12.0 → 7.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/autonomy/loki CHANGED
@@ -13111,6 +13111,9 @@ main() {
13111
13111
  checkpoint|cp)
13112
13112
  cmd_checkpoint "$@"
13113
13113
  ;;
13114
+ rollback)
13115
+ cmd_rollback "$@"
13116
+ ;;
13114
13117
  council)
13115
13118
  cmd_council "$@"
13116
13119
  ;;
@@ -13205,6 +13208,9 @@ main() {
13205
13208
  docs)
13206
13209
  cmd_docs "$@"
13207
13210
  ;;
13211
+ wiki)
13212
+ cmd_wiki "$@"
13213
+ ;;
13208
13214
  magic)
13209
13215
  cmd_magic "$@"
13210
13216
  ;;
@@ -16435,12 +16441,19 @@ SHOW_EOF
16435
16441
  local restored=0
16436
16442
  for item in "$cp_dir"/*; do
16437
16443
  [ -e "$item" ] || continue
16438
- local name=$(basename "$item")
16444
+ local name
16445
+ name=$(basename "$item")
16439
16446
  [ "$name" = "metadata.json" ] && continue
16447
+ # R6: do not restore the worktree-snapshot sidecar as state.
16448
+ [ "$name" = "worktree-snapshot.txt" ] && continue
16440
16449
 
16441
16450
  if [ -d "$item" ]; then
16442
- rm -rf ".loki/$name"
16443
- cp -r "$item" ".loki/$name" 2>/dev/null && restored=$((restored + 1))
16451
+ # R6 data-loss fix: NEVER `rm -rf ".loki/$name"` -- the
16452
+ # checkpoint store lives under .loki/state/checkpoints/, so
16453
+ # blowing away .loki/state/ would destroy every checkpoint
16454
+ # (including this one). Merge-copy directory contents instead.
16455
+ mkdir -p ".loki/$name"
16456
+ cp -r "$item"/. ".loki/$name"/ 2>/dev/null && restored=$((restored + 1))
16444
16457
  else
16445
16458
  cp "$item" ".loki/$name" 2>/dev/null && restored=$((restored + 1))
16446
16459
  fi
@@ -16448,17 +16461,28 @@ SHOW_EOF
16448
16461
 
16449
16462
  echo -e " Restored: ${GREEN}$restored${NC} state items from $cp_id"
16450
16463
 
16451
- # Show git info for manual code rollback
16452
- local cp_sha=$(_CP_METADATA="$metadata" python3 -c "import json, os; d=json.load(open(os.environ['_CP_METADATA'])); print(d.get('git_sha','unknown'))" 2>/dev/null)
16453
- if [ -n "$cp_sha" ] && [ "$cp_sha" != "unknown" ] && [ "$cp_sha" != "not-a-git-repo" ]; then
16464
+ # Show how to also restore code. R6: prefer the anchored working-tree
16465
+ # snapshot over `git reset --hard <git_sha>`. git_sha is HEAD (the last
16466
+ # commit), and Loki does not commit per iteration, so a hard reset
16467
+ # discards the iteration's work instead of reconstructing it -- the old
16468
+ # hint was misleading.
16469
+ if git rev-parse --verify "refs/loki/cp/${cp_id}" >/dev/null 2>&1; then
16454
16470
  echo ""
16455
- echo -e " ${YELLOW}Note:${NC} Session state has been restored, but code is unchanged."
16456
- echo " To also roll back code to the checkpoint's git state:"
16471
+ echo -e " ${YELLOW}Note:${NC} Session state restored. Code is unchanged."
16472
+ echo " To also restore the working tree to this checkpoint's snapshot:"
16457
16473
  echo ""
16458
- echo -e " ${DIM}git reset --hard ${cp_sha:0:8}${NC}"
16474
+ echo -e " ${DIM}git stash apply refs/loki/cp/${cp_id}${NC}"
16459
16475
  echo ""
16460
- echo -e " ${RED}Warning:${NC} git reset --hard will discard uncommitted changes."
16461
- echo " Consider 'git stash' first to preserve current work."
16476
+ echo -e " ${DIM}(restores tracked files; newly-added files are not removed)${NC}"
16477
+ else
16478
+ local cp_sha
16479
+ cp_sha=$(_CP_METADATA="$metadata" python3 -c "import json, os; d=json.load(open(os.environ['_CP_METADATA'])); print(d.get('git_sha','unknown'))" 2>/dev/null)
16480
+ if [ -n "$cp_sha" ] && [ "$cp_sha" != "unknown" ] && [ "$cp_sha" != "not-a-git-repo" ]; then
16481
+ echo ""
16482
+ echo -e " ${YELLOW}Note:${NC} Session state restored, but no working-tree snapshot"
16483
+ echo " was captured for this checkpoint, so code changes since the last"
16484
+ echo -e " commit (${cp_sha:0:8}) are not restorable from here."
16485
+ fi
16462
16486
  fi
16463
16487
  ;;
16464
16488
 
@@ -16494,6 +16518,157 @@ SHOW_EOF
16494
16518
  esac
16495
16519
  }
16496
16520
 
16521
+ # R6: one-command rollback. Top-level, obvious entry point that mirrors the Bun
16522
+ # `loki rollback` subcommands (list/show/to/latest) so both routes are at parity.
16523
+ # Restore is destructive on .loki/ state, so it ALWAYS captures a forced
16524
+ # pre-rollback snapshot first (re-undoability invariant) and then glob-restores
16525
+ # whatever the checkpoint dir contains (works across all three checkpoint writers).
16526
+ cmd_rollback() {
16527
+ local subcommand="${1:-help}"
16528
+ shift 2>/dev/null || true
16529
+
16530
+ local checkpoints_dir=".loki/state/checkpoints"
16531
+ local index_file="$checkpoints_dir/index.jsonl"
16532
+
16533
+ # Resolve the most recent checkpoint id from on-disk cp-*/chk-* dirs, sorted
16534
+ # by mtime (newest last). Mirrors the Bun "latest = last entry" semantics but
16535
+ # is robust to the three differing id formats.
16536
+ _rollback_latest_id() {
16537
+ [ -d "$checkpoints_dir" ] || return 1
16538
+ ls -1dt "$checkpoints_dir"/*/ 2>/dev/null | head -1 | sed 's#/$##' | xargs -I{} basename {} 2>/dev/null
16539
+ }
16540
+
16541
+ # Force a pre-rollback snapshot of current state, then glob-restore the target.
16542
+ # Delegates the actual restore to `cmd_checkpoint rollback`, which already
16543
+ # glob-restores and is shared with the manual path (no duplication).
16544
+ _rollback_restore() {
16545
+ local target_id="$1"
16546
+ local want_code="$2"
16547
+
16548
+ # Validate id (defense in depth; cmd_checkpoint validates again).
16549
+ if [[ ! "$target_id" =~ ^[a-zA-Z0-9_-]+$ ]]; then
16550
+ echo -e "${RED}Error: Invalid checkpoint ID${NC}"
16551
+ return 1
16552
+ fi
16553
+ if [ ! -d "$checkpoints_dir/$target_id" ]; then
16554
+ echo -e "${RED}Error: Checkpoint not found: $target_id${NC}"
16555
+ echo "Run 'loki rollback list' to see available checkpoints."
16556
+ return 1
16557
+ fi
16558
+
16559
+ # Re-undoability: snapshot current state before overwriting it.
16560
+ local pre_id
16561
+ pre_id="rb-pre-$(date -u '+%Y%m%d-%H%M%S')"
16562
+ local pre_dir="$checkpoints_dir/$pre_id"
16563
+ mkdir -p "$pre_dir"
16564
+ local saved=0
16565
+ for item in .loki/session.json .loki/dashboard-state.json .loki/CONTINUITY.md .loki/autonomy-state.json .loki/state .loki/queue; do
16566
+ if [ -e "$item" ]; then
16567
+ cp -r "$item" "$pre_dir/" 2>/dev/null && saved=$((saved + 1))
16568
+ fi
16569
+ done
16570
+ _RB_PID="$pre_id" _RB_TS="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" _RB_DIR="$pre_dir" \
16571
+ _RB_INDEX="$index_file" _RB_CHKDIR="$checkpoints_dir" python3 << 'RB_PRE_EOF' 2>/dev/null || true
16572
+ import json, os
16573
+ meta = {"id": os.environ["_RB_PID"], "timestamp": os.environ["_RB_TS"],
16574
+ "message": "pre-rollback snapshot", "created_by": "loki rollback"}
16575
+ d = os.environ["_RB_DIR"]
16576
+ os.makedirs(d, exist_ok=True)
16577
+ with open(os.path.join(d, "metadata.json"), "w") as f:
16578
+ json.dump(meta, f, indent=2)
16579
+ os.makedirs(os.environ["_RB_CHKDIR"], exist_ok=True)
16580
+ with open(os.environ["_RB_INDEX"], "a") as f:
16581
+ f.write(json.dumps({"id": meta["id"], "timestamp": meta["timestamp"], "message": meta["message"]}) + "\n")
16582
+ RB_PRE_EOF
16583
+ echo -e " ${DIM}Saved prior state as ${pre_id} (undo this rollback with: loki rollback to ${pre_id})${NC}"
16584
+
16585
+ # Perform the actual state restore via the shared glob-restore path.
16586
+ cmd_checkpoint rollback "$target_id"
16587
+
16588
+ # Optional code restore from the anchored working-tree snapshot.
16589
+ if [ "$want_code" = "1" ]; then
16590
+ if git rev-parse --verify "refs/loki/cp/${target_id}" >/dev/null 2>&1; then
16591
+ echo ""
16592
+ echo -e " ${YELLOW}Restoring working tree from snapshot refs/loki/cp/${target_id}...${NC}"
16593
+ if git stash apply "refs/loki/cp/${target_id}" 2>/dev/null; then
16594
+ echo -e " ${GREEN}Working tree restored.${NC} (tracked files only; newly-added files were not removed)"
16595
+ else
16596
+ echo -e " ${RED}Could not apply snapshot cleanly.${NC} Resolve conflicts, or run: git stash apply refs/loki/cp/${target_id}"
16597
+ fi
16598
+ else
16599
+ echo ""
16600
+ echo -e " ${YELLOW}No working-tree snapshot anchored for ${target_id}; code not restored.${NC}"
16601
+ fi
16602
+ fi
16603
+ }
16604
+
16605
+ case "$subcommand" in
16606
+ list|ls)
16607
+ cmd_checkpoint list
16608
+ ;;
16609
+ show)
16610
+ cmd_checkpoint show "$@"
16611
+ ;;
16612
+ to)
16613
+ local target="${1:-}"
16614
+ local want_code=0
16615
+ shift 2>/dev/null || true
16616
+ for a in "$@"; do [ "$a" = "--code" ] && want_code=1; done
16617
+ if [ -z "$target" ]; then
16618
+ echo -e "${RED}Error: Specify a checkpoint ID${NC}"
16619
+ echo "Usage: loki rollback to <id> [--code]"
16620
+ echo "Run 'loki rollback list' to see available checkpoints."
16621
+ return 1
16622
+ fi
16623
+ echo -e "${BOLD}Rolling back to checkpoint: $target${NC}"
16624
+ echo ""
16625
+ _rollback_restore "$target" "$want_code"
16626
+ ;;
16627
+ latest)
16628
+ local want_code=0
16629
+ for a in "$@"; do [ "$a" = "--code" ] && want_code=1; done
16630
+ local latest_id
16631
+ latest_id=$(_rollback_latest_id)
16632
+ if [ -z "$latest_id" ]; then
16633
+ echo -e "${RED}No checkpoints found to roll back to.${NC}"
16634
+ return 1
16635
+ fi
16636
+ echo -e "${BOLD}Rolling back to latest checkpoint: $latest_id${NC}"
16637
+ echo ""
16638
+ _rollback_restore "$latest_id" "$want_code"
16639
+ ;;
16640
+ help|--help|-h)
16641
+ echo -e "${BOLD}loki rollback${NC} - One-command rollback to a checkpoint"
16642
+ echo ""
16643
+ echo "Restore .loki/ state and iteration/conversation context to a"
16644
+ echo "previous checkpoint. Every rollback first saves your current state"
16645
+ echo "as a pre-rollback snapshot, so you can always undo the undo."
16646
+ echo ""
16647
+ echo "Usage: loki rollback <command> [args]"
16648
+ echo ""
16649
+ echo "Commands:"
16650
+ echo " list List recent checkpoints"
16651
+ echo " show <id> Show checkpoint details"
16652
+ echo " to <id> [--code] Restore to checkpoint <id>"
16653
+ echo " latest [--code] Restore to the most recent checkpoint"
16654
+ echo ""
16655
+ echo " --code Also restore the working tree from the anchored"
16656
+ echo " git snapshot (tracked files only; overwrites them)."
16657
+ echo ""
16658
+ echo "Examples:"
16659
+ echo " loki rollback list"
16660
+ echo " loki rollback latest"
16661
+ echo " loki rollback to cp-3-1717000000"
16662
+ echo " loki rollback to cp-3-1717000000 --code"
16663
+ ;;
16664
+ *)
16665
+ echo -e "${RED}Unknown rollback command: $subcommand${NC}"
16666
+ echo "Run 'loki rollback help' for usage."
16667
+ return 1
16668
+ ;;
16669
+ esac
16670
+ }
16671
+
16497
16672
  # Completion Council management
16498
16673
  cmd_council() {
16499
16674
  local subcommand="${1:-status}"
@@ -22633,6 +22808,124 @@ run_debate(
22633
22808
  log_info "Debate complete"
22634
22809
  }
22635
22810
 
22811
+ # =============================================================================
22812
+ # loki wiki -- auto-generated, cited per-project codebase wiki + Q&A (R5).
22813
+ #
22814
+ # Loki's answer to Devin DeepWiki: a persistent, queryable wiki built from the
22815
+ # codebase, where every section cites the real source files it came from, plus
22816
+ # a grounded `ask` that returns cited answers (file:line). Heavy work lives in
22817
+ # the Python core (autonomy/lib/wiki-generator.py, wiki-ask.py, wiki_index.py);
22818
+ # this is a thin dispatcher, mirroring how cmd_proof delegates to the proof
22819
+ # generator. Generation is incremental (skips when the codebase is unchanged).
22820
+ # =============================================================================
22821
+ cmd_wiki() {
22822
+ local subcmd="${1:-}"
22823
+ shift 2>/dev/null || true
22824
+
22825
+ local lib_dir="${_LOKI_SCRIPT_DIR}/lib"
22826
+
22827
+ case "$subcmd" in
22828
+ generate) _wiki_generate "$lib_dir" "$@" ;;
22829
+ show) _wiki_show "$@" ;;
22830
+ ask) _wiki_ask "$lib_dir" "$@" ;;
22831
+ --help|-h|help|"")
22832
+ echo -e "${BOLD}loki wiki${NC} - Auto-generated, cited codebase wiki + Q&A"
22833
+ echo ""
22834
+ echo "Usage: loki wiki <command> [options]"
22835
+ echo ""
22836
+ echo "Commands:"
22837
+ echo " generate [path] [--force] Build/refresh the cited wiki in .loki/wiki/"
22838
+ echo " show [section] Print the wiki (or one section: architecture|modules|data-flow)"
22839
+ echo " ask \"<question>\" Cited answer grounded in the codebase (file:line)"
22840
+ echo ""
22841
+ echo "Each wiki section cites the real source files it was built from."
22842
+ echo "Generation is incremental: it skips when the codebase is unchanged."
22843
+ echo ""
22844
+ echo "Examples:"
22845
+ echo " loki wiki generate # build wiki for current project"
22846
+ echo " loki wiki show architecture # show one section"
22847
+ echo " loki wiki ask \"how does the cli dispatch commands\""
22848
+ return 0
22849
+ ;;
22850
+ *)
22851
+ log_error "Unknown wiki command: $subcmd"
22852
+ echo "Run 'loki wiki --help' for usage."
22853
+ return 1
22854
+ ;;
22855
+ esac
22856
+ }
22857
+
22858
+ _wiki_generate() {
22859
+ local lib_dir="$1"; shift
22860
+ if ! command -v python3 >/dev/null 2>&1; then
22861
+ log_error "python3 is required for 'loki wiki generate'"
22862
+ return 1
22863
+ fi
22864
+ python3 "$lib_dir/wiki-generator.py" "$@"
22865
+ }
22866
+
22867
+ _wiki_show() {
22868
+ local section=""
22869
+ local target="."
22870
+ while [[ $# -gt 0 ]]; do
22871
+ case "$1" in
22872
+ --help|-h)
22873
+ echo "Usage: loki wiki show [section]"
22874
+ echo "Sections: architecture, modules, data-flow"
22875
+ return 0
22876
+ ;;
22877
+ -*) log_error "Unknown option: $1"; return 1 ;;
22878
+ *) section="$1"; shift ;;
22879
+ esac
22880
+ done
22881
+ local wiki_dir="$target/.loki/wiki"
22882
+ if [ ! -d "$wiki_dir" ]; then
22883
+ log_error "No wiki found. Run 'loki wiki generate' first."
22884
+ return 1
22885
+ fi
22886
+ if [ -n "$section" ]; then
22887
+ local f="$wiki_dir/${section}.md"
22888
+ if [ ! -f "$f" ]; then
22889
+ log_error "No such section: $section (try: architecture, modules, data-flow)"
22890
+ return 1
22891
+ fi
22892
+ cat "$f"
22893
+ else
22894
+ if [ -f "$wiki_dir/index.md" ]; then
22895
+ cat "$wiki_dir/index.md"
22896
+ else
22897
+ log_error "Wiki index not found. Run 'loki wiki generate'."
22898
+ return 1
22899
+ fi
22900
+ fi
22901
+ }
22902
+
22903
+ _wiki_ask() {
22904
+ local lib_dir="$1"; shift
22905
+ local question=""
22906
+ local extra=()
22907
+ while [[ $# -gt 0 ]]; do
22908
+ case "$1" in
22909
+ --help|-h)
22910
+ echo "Usage: loki wiki ask \"<question>\" [--json] [--k N]"
22911
+ return 0
22912
+ ;;
22913
+ --json|--quiet) extra+=("$1"); shift ;;
22914
+ --k) extra+=("--k" "${2:-6}"); shift 2 ;;
22915
+ *) if [ -z "$question" ]; then question="$1"; else question="$question $1"; fi; shift ;;
22916
+ esac
22917
+ done
22918
+ if [ -z "$question" ]; then
22919
+ log_error "Provide a question: loki wiki ask \"how does X work\""
22920
+ return 1
22921
+ fi
22922
+ if ! command -v python3 >/dev/null 2>&1; then
22923
+ log_error "python3 is required for 'loki wiki ask'"
22924
+ return 1
22925
+ fi
22926
+ python3 "$lib_dir/wiki-ask.py" --question "$question" "${extra[@]}"
22927
+ }
22928
+
22636
22929
  cmd_magic() {
22637
22930
  local subcmd="${1:-help}"
22638
22931
  shift 2>/dev/null || true
package/autonomy/run.sh CHANGED
@@ -7443,10 +7443,18 @@ create_checkpoint() {
7443
7443
 
7444
7444
  mkdir -p "$checkpoint_dir"
7445
7445
 
7446
- # Only checkpoint if there are uncommitted changes
7447
- if git diff --quiet 2>/dev/null && git diff --cached --quiet 2>/dev/null; then
7448
- log_info "No uncommitted changes to checkpoint"
7449
- return 0
7446
+ # Only checkpoint if there are uncommitted changes.
7447
+ # R6: _LOKI_CP_FORCE=1 bypasses this guard. Used by rollback to guarantee a
7448
+ # pre-rollback snapshot of .loki/ state even when the git tree is clean (the
7449
+ # .loki/ state files about to be overwritten are not git-tracked, so the
7450
+ # clean-tree guard would otherwise skip the safety snapshot). Mirrors the
7451
+ # Bun `forceCreate` seam in checkpoint.ts.
7452
+ if [ "${_LOKI_CP_FORCE:-0}" != "1" ]; then
7453
+ if git diff --quiet 2>/dev/null && git diff --cached --quiet 2>/dev/null; then
7454
+ log_info "No uncommitted changes to checkpoint"
7455
+ _LAST_CHECKPOINT_ID=""
7456
+ return 0
7457
+ fi
7450
7458
  fi
7451
7459
 
7452
7460
  # Capture git state
@@ -7465,7 +7473,9 @@ create_checkpoint() {
7465
7473
 
7466
7474
  # Copy critical state files (lightweight -- not full .loki/)
7467
7475
  # BUG-ST-009: Include autonomy-state.json in checkpoint backup
7468
- for f in state/orchestrator.json autonomy-state.json queue/pending.json queue/completed.json queue/in-progress.json queue/current-task.json; do
7476
+ # R6: Include CONTINUITY.md so a rollback also restores iteration/conversation
7477
+ # handoff context, not just machine state. Mirrors Bun COPIED_FILES.
7478
+ for f in state/orchestrator.json autonomy-state.json queue/pending.json queue/completed.json queue/in-progress.json queue/current-task.json CONTINUITY.md; do
7469
7479
  if [ -f ".loki/$f" ]; then
7470
7480
  local target_dir="$cp_dir/$(dirname "$f")"
7471
7481
  mkdir -p "$target_dir"
@@ -7473,6 +7483,23 @@ create_checkpoint() {
7473
7483
  fi
7474
7484
  done
7475
7485
 
7486
+ # R6: capture a real working-tree snapshot so code can be truly undone later.
7487
+ # Loki does not commit per iteration, so git_sha (HEAD) cannot reconstruct
7488
+ # this iteration's working tree. `git stash create` builds a commit object
7489
+ # capturing tracked changes WITHOUT disturbing the tree; we then anchor it
7490
+ # under refs/loki/cp/<id> so `git gc` cannot prune the dangling commit. The
7491
+ # snapshot sha goes in a sidecar (worktree-snapshot.txt), NOT metadata.json,
7492
+ # to preserve byte-for-byte parity with the Bun port.
7493
+ # Honest limit: captures tracked changes only (not untracked/ignored files).
7494
+ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
7495
+ local snap_sha
7496
+ snap_sha=$(git stash create "loki checkpoint ${checkpoint_id}" 2>/dev/null || echo "")
7497
+ if [ -n "$snap_sha" ]; then
7498
+ git update-ref "refs/loki/cp/${checkpoint_id}" "$snap_sha" 2>/dev/null \
7499
+ && printf '%s\n' "$snap_sha" > "$cp_dir/worktree-snapshot.txt" 2>/dev/null || true
7500
+ fi
7501
+ fi
7502
+
7476
7503
  # Write checkpoint metadata (use python3 json.dumps for safe serialization)
7477
7504
  local phase_val
7478
7505
  phase_val=$(cat .loki/state/orchestrator.json 2>/dev/null | python3 -c 'import sys,json; print(json.load(sys.stdin).get("currentPhase","unknown"))' 2>/dev/null || echo 'unknown')
@@ -7531,6 +7558,10 @@ print(json.dumps({'id':m['id'],'ts':m['timestamp'],'iter':m['iteration'],'task':
7531
7558
  fi
7532
7559
 
7533
7560
  log_info "Checkpoint created: ${checkpoint_id} (git: ${git_sha:0:8})"
7561
+ # R6: expose the id via a global so callers (rollback, run loop) can reference
7562
+ # it without parsing stdout (log_info writes to stdout, so command-substitution
7563
+ # capture would include log lines).
7564
+ _LAST_CHECKPOINT_ID="$checkpoint_id"
7534
7565
  }
7535
7566
 
7536
7567
  rollback_to_checkpoint() {
@@ -7558,11 +7589,18 @@ rollback_to_checkpoint() {
7558
7589
 
7559
7590
  log_warn "Rolling back to checkpoint: ${checkpoint_id}"
7560
7591
 
7561
- # Create a pre-rollback checkpoint first
7562
- create_checkpoint "pre-rollback snapshot" "rollback"
7592
+ # R6 re-undoability invariant: force a pre-rollback snapshot of CURRENT state
7593
+ # before overwriting, even if the git tree is clean (the .loki/ state we are
7594
+ # about to clobber is not git-tracked). _LOKI_CP_FORCE bypasses the clean-tree
7595
+ # guard. Capture the snapshot id so we can tell the user how to undo the undo.
7596
+ _LOKI_CP_FORCE=1 create_checkpoint "pre-rollback snapshot (before restoring ${checkpoint_id})" "rollback"
7597
+ local pre_rollback_id="${_LAST_CHECKPOINT_ID:-}"
7598
+ if [ -n "$pre_rollback_id" ]; then
7599
+ log_info "Saved prior state as ${pre_rollback_id} (undo this rollback with: loki rollback to ${pre_rollback_id})"
7600
+ fi
7563
7601
 
7564
- # Restore state files
7565
- for f in state/orchestrator.json queue/pending.json queue/completed.json queue/in-progress.json queue/current-task.json; do
7602
+ # Restore state files (R6: CONTINUITY.md restores iteration/conversation context)
7603
+ for f in state/orchestrator.json queue/pending.json queue/completed.json queue/in-progress.json queue/current-task.json CONTINUITY.md; do
7566
7604
  if [ -f "${cp_dir}/${f}" ]; then
7567
7605
  local target_dir=".loki/$(dirname "$f")"
7568
7606
  mkdir -p "$target_dir"
@@ -7599,9 +7637,17 @@ print(json.dumps({'event':'rollback','checkpoint':os.environ['_RB_CPID'],'git_sh
7599
7637
 
7600
7638
  log_info "State files restored from checkpoint: ${checkpoint_id}"
7601
7639
 
7602
- if [ -n "$git_sha" ] && [ "$git_sha" != "no-git" ]; then
7603
- log_info "Git SHA at checkpoint: ${git_sha}"
7604
- log_info "To rollback code: git reset --hard ${git_sha}"
7640
+ # R6: the prior hint `git reset --hard ${git_sha}` was MISLEADING. git_sha is
7641
+ # HEAD (the last commit), and Loki does not commit per iteration, so a hard
7642
+ # reset would discard the iteration's work rather than reconstruct it. The
7643
+ # correct, durable recovery is the anchored working-tree snapshot, if present.
7644
+ if [ -f "${cp_dir}/worktree-snapshot.txt" ]; then
7645
+ log_info "To also restore the working tree to this checkpoint:"
7646
+ log_info " git stash apply refs/loki/cp/${checkpoint_id}"
7647
+ elif [ -n "$git_sha" ] && [ "$git_sha" != "no-git" ]; then
7648
+ log_info "Git SHA at checkpoint (last commit): ${git_sha}"
7649
+ log_info "Note: no working-tree snapshot was captured for this checkpoint;"
7650
+ log_info "code changes since the last commit are not restorable from here."
7605
7651
  fi
7606
7652
  }
7607
7653
 
@@ -12001,6 +12047,10 @@ if __name__ == "__main__":
12001
12047
 
12002
12048
  # Checkpoint after each iteration (v5.57.0)
12003
12049
  create_checkpoint "iteration-${ITERATION_COUNT} complete" "iteration-${ITERATION_COUNT}"
12050
+ # R6: prominent "you can safely undo this" signal so users run boldly.
12051
+ if [ -n "${_LAST_CHECKPOINT_ID:-}" ]; then
12052
+ log_info "Safety net: checkpoint ${_LAST_CHECKPOINT_ID} saved. Undo this iteration with: loki rollback to ${_LAST_CHECKPOINT_ID}"
12053
+ fi
12004
12054
 
12005
12055
  # Quality gates (v6.10.0 - escalation ladder)
12006
12056
  log_step "Post-iteration: running quality gates..."
package/bin/loki CHANGED
@@ -116,7 +116,7 @@ fi
116
116
  # Two-token routes (provider show/list, memory list/index) match on the first
117
117
  # token only; the Bun dispatcher handles subcommand routing internally.
118
118
  case "${1:-}" in
119
- version|--version|-v|status|stats|doctor|provider|memory|rollback|internal|kpis|proof)
119
+ version|--version|-v|status|stats|doctor|provider|memory|rollback|internal|kpis|proof|wiki)
120
120
  # v7.5.2: rollback added (wires loki-ts/src/commands/rollback.ts).
121
121
  # v7.5.3: internal added for autonomy/run.sh phase1-hooks calls.
122
122
  # v7.5.28: kpis added (Phase K MVP: read-only KPI snapshot).
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.12.0"
10
+ __version__ = "7.14.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try: