123module cli45import json26import src.fmt7import src.history8import src.snapshot9101112pub fn cmd_save(raw []string, is_checkpoint bool) int {13 verb := if is_checkpoint { 'checkpoint' } else { 'save' }14 args := parse(verb, raw, Options{15 values: ['agent', 'session']16 flags: ['json']17 }) or { return usage_error(err.msg()) }18 message := args.positional_at(0)19 if message == '' {20 example := if is_checkpoint {21 'before combine replay engine'22 } else {23 'fix off-by-one in chunk boundary'24 }25 return usage_error('${verb} needs a message, e.g. koh ${verb} "${example}"')26 }27 store := open_store() or { return refuse(args.json(), err.msg()) }28 defer {29 store.close()30 }31 mut hashing := reading()32 walked := snapshot.walk_report(store, store.root, snapshot.load(store.root), mut hashing) or {33 return refuse(args.json(), err.msg())34 }35 save := history.save_worktree(store, store.root, snapshot.load(store.root), history.SaveInput{36 message: message37 agent: args.value('agent')38 session: args.value('session')39 is_checkpoint: is_checkpoint40 storing: fn (done int, total int) {41 if done % 100 == 1 || done == total {42 working('storing ${done} / ${total} files')43 }44 }45 }) or { return refuse(args.json(), err.msg()) }4647 files := history.files_of(store, save.id) or { []snapshot.FileEntry{} }48 if args.json() {49 emit_json(json2.encode(SaveOut{50 id: save.id51 parent: save.parent52 message: save.message53 created_at: save.created_at54 machine: save.machine55 is_checkpoint: save.is_checkpoint56 agent: save.agent57 session: save.session58 files: files.len59 size_bytes: save.size_bytes60 unsafe_paths: walked.unsafe_paths61 }))62 auto_offer(store, true)63 return 064 }65 for path in walked.unsafe_paths {66 warn('not saved: unsafe name ${path}')67 }68 ok('saved [${save.id}] ${save.message}')69 mut detail := []string{}70 if save.is_checkpoint {71 detail << 'checkpoint'72 }73 detail << fmt.stamp(save.created_at)74 detail << fmt.plural(files.len, 'file')75 detail << fmt.human_bytes(save.size_bytes)76 note(detail.join(' · '))77 mut attribution := []string{}78 if save.agent != '' {79 attribution << 'agent: ${save.agent}'80 }81 if save.session != '' {82 attribution << 'session: ${save.session}'83 }84 if attribution.len > 0 {85 note(attribution.join(' · '))86 }87 auto_offer(store, false)88 return 089}90919293struct SaveOut {94 id string95 parent string96 message string97 created_at i6498 machine string99 is_checkpoint bool100 agent string101 session string102 files int103 size_bytes i64104 unsafe_paths []string105}