Download scripts/update_context_hook.py from kabiriitb/rockglacierNET: direct link, hf CLI and curl.
- Browser
- Download file 6.85 kB
-
https://huggingface.co/kabiriitb/rockglacierNET/resolve/main/scripts/update_context_hook.py
- Command line
-
hf download hf://kabiriitb/rockglacierNET/scripts/update_context_hook.py
-
curl -L -o update_context_hook.py https://huggingface.co/kabiriitb/rockglacierNET/resolve/main/scripts/update_context_hook.py
6.85 kB
| #!/usr/bin/env python3 | |
| """ | |
| Stop-hook context updater for RockGlacierNet. | |
| Runs as a plain shell command β no Claude Code permission system involved. | |
| Appends a timestamped session-end entry to agent.md and updates | |
| project_state.md with current git + training state. | |
| Called by .claude/settings.json Stop hook. | |
| """ | |
| import os | |
| import sys | |
| import subprocess | |
| import glob | |
| from datetime import datetime, timezone | |
| ROOT = "/data1/nasiruddink/rockglacier/RockGlacierNet" | |
| AGENT = os.path.join(ROOT, "agent.md") | |
| MEMORY = "/home/nasiruddink/.claude/projects/-data1-nasiruddink-rockglacier-RockGlacierNet/memory" | |
| STATE = os.path.join(MEMORY, "project_state.md") | |
| MEMIDX = os.path.join(MEMORY, "MEMORY.md") | |
| def run(cmd): | |
| try: | |
| return subprocess.check_output(cmd, shell=True, cwd=ROOT, | |
| stderr=subprocess.DEVNULL, | |
| text=True).strip() | |
| except Exception: | |
| return "" | |
| def git_state(): | |
| log = run("git log --oneline -5") | |
| status = run("git status --short") | |
| return log, status | |
| def training_state(): | |
| csv = os.path.join(ROOT, "outputs/checkpoints/training_history.csv") | |
| ckpt = os.path.join(ROOT, "outputs/checkpoints/best_model.weights.h5") | |
| has_ckpt = os.path.exists(ckpt) | |
| if not os.path.exists(csv): | |
| return None, has_ckpt | |
| try: | |
| # Read last row without pandas dependency | |
| with open(csv) as f: | |
| lines = [l.strip() for l in f if l.strip()] | |
| if len(lines) < 2: | |
| return None, has_ckpt | |
| header = lines[0].split(",") | |
| last = lines[-1].split(",") | |
| row = dict(zip(header, last)) | |
| return row, has_ckpt | |
| except Exception: | |
| return None, has_ckpt | |
| def patch_counts(): | |
| counts = {} | |
| for split in ("train", "val", "test"): | |
| d = os.path.join(ROOT, "patches", split, "features") | |
| if os.path.isdir(d): | |
| counts[split] = len(glob.glob(os.path.join(d, "*.npy"))) | |
| else: | |
| counts[split] = 0 | |
| return counts | |
| def append_agent_md(now_str, log, status, row, has_ckpt, patches): | |
| lines = [ | |
| "", | |
| "---", | |
| "", | |
| f"## Session β {now_str}", | |
| "", | |
| "*(Auto-recorded by Stop hook β run `/update-context` for full intelligent summary)*", | |
| "", | |
| ] | |
| lines += ["**Git state:**"] | |
| if log: | |
| for l in log.splitlines(): | |
| lines.append(f"- {l}") | |
| else: | |
| lines.append("- (no commits)") | |
| if status: | |
| lines.append(f"- Modified: {', '.join(status.splitlines()[:5])}") | |
| lines.append("") | |
| lines += ["**Training state:**"] | |
| lines.append(f"- Checkpoint: {'exists' if has_ckpt else 'NOT FOUND'} (best_model.weights.h5)") | |
| if row: | |
| epoch = row.get("epoch", "?") | |
| iou = row.get("val_iou_m", row.get("iou_m", "?")) | |
| dice = row.get("val_dice_coef", "?") | |
| loss = row.get("val_loss", row.get("loss", "?")) | |
| prec = row.get("val_precision_m", "?") | |
| rec = row.get("val_recall_m", "?") | |
| lines.append(f"- Last epoch: {epoch} | val_iou={iou} val_dice={dice} val_loss={loss}") | |
| lines.append(f"- val_precision={prec} val_recall={rec}") | |
| else: | |
| lines.append("- No training history found") | |
| lines.append("") | |
| if any(patches.values()): | |
| lines += ["**Patches:**"] | |
| for sp, n in patches.items(): | |
| lines.append(f"- {sp}: {n} patches") | |
| lines.append("") | |
| lines.append("**Next steps:** *(fill in manually or run /update-context)*") | |
| lines.append("") | |
| entry = "\n".join(lines) | |
| os.makedirs(os.path.dirname(AGENT) if os.path.dirname(AGENT) else ".", exist_ok=True) | |
| with open(AGENT, "a") as f: | |
| f.write(entry) | |
| def update_memory(now_str, row, has_ckpt, log): | |
| os.makedirs(MEMORY, exist_ok=True) | |
| # --- MEMORY.md index --- | |
| mem_content = f"""# RockGlacierNet Memory Index | |
| **Last Updated**: {now_str} | |
| - [project_state.md](project_state.md) β Full pipeline, model architecture, dataset stats, training metrics | |
| """ | |
| with open(MEMIDX, "w") as f: | |
| f.write(mem_content) | |
| # --- project_state.md --- | |
| iou = dice = loss = epoch = "unknown" | |
| if row: | |
| epoch = row.get("epoch", "?") | |
| iou = row.get("val_iou_m", row.get("iou_m", "?")) | |
| dice = row.get("val_dice_coef", "?") | |
| loss = row.get("val_loss", row.get("loss", "?")) | |
| last_commit = log.splitlines()[0] if log else "unknown" | |
| state_content = f"""--- | |
| name: project-state | |
| description: Full pipeline, model architecture, dataset stats, and current training results for RockGlacierNet | |
| metadata: | |
| type: project | |
| lastUpdated: "{now_str}" | |
| --- | |
| ## What this project does | |
| Binary semantic segmentation of rock glaciers in Sikkim Himalayas. | |
| Input: 12-band GeoTIFFs (Sentinel-2 + DEM). Output: binary glacier/background mask. | |
| ## Current Training State (auto-updated {now_str}) | |
| - Last commit: {last_commit} | |
| - Checkpoint: {'EXISTS' if has_ckpt else 'MISSING'} β outputs/checkpoints/best_model.weights.h5 | |
| - Last epoch: {epoch} | |
| - val_iou_m: {iou} | val_dice_coef: {dice} | val_loss: {loss} | |
| ## Baseline (old 128Γ128 model, 70 epochs) | |
| - val_iou=0.5825, val_dice=0.7618, precision=0.6233, recall=0.8990, f1=0.7362 | |
| ## Current Result (new 64Γ64 model, 58 epochs β REGRESSION) | |
| - val_iou=0.3308 β IoU dropped 44% vs baseline | |
| - Root cause: 64px patches too small for spatial context (glaciers are 40β60px wide) | |
| - Fix needed: increase patch_size to 128 or use full-image variable-size training | |
| ## Active Architecture | |
| - Model: ResidualSwinUNET_64x64 (models/swin_unet.py) | |
| - Input: (64, 64, 12) β Output: (64, 64, 1) sigmoid | |
| - Encoder: 3 pool layers, filters 32β64β128β256 | |
| - Bottleneck: 8Γ8Γ256 + 2Γ Swin transformer blocks (64 tokens) | |
| - Loss: Focal(Ξ±=0.75,Ξ³=2.0)Γ2 + Dice | Optimizer: Adam lr=1e-4 | |
| ## Pipeline Run Order | |
| 1. python preprocess/extract_patches.py | |
| 2. python preprocess/validate_alignment.py | |
| 3. python train.py | |
| 4. python evaluate.py | |
| ## Key Files | |
| - preprocess/patch_config.py β patch_size=64, stride=32, bg_keep=0.25 | |
| - data_generator.py β loads .npy patches, z-score normalizes | |
| - outputs/checkpoints/training_history.csv β per-epoch metrics | |
| - agent.md β full session log and project index | |
| """ | |
| with open(STATE, "w") as f: | |
| f.write(state_content) | |
| def main(): | |
| now_str = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") | |
| log, status = git_state() | |
| row, has_ckpt = training_state() | |
| patches = patch_counts() | |
| append_agent_md(now_str, log, status, row, has_ckpt, patches) | |
| update_memory(now_str, row, has_ckpt, log) | |
| print(f"Context updated β agent.md entry appended ({now_str}).") | |
| if __name__ == "__main__": | |
| main() | |