123module cli45import json26import src.fmt7import src.history8import src.snapshot910pub fn cmd_save(raw []string, is_checkpoint bool) int {11 verb := if is_checkpoint { 'checkpoint' } else { 'save' }12 args := parse(verb, raw, Options{13 values: ['agent', 'session']14 flags: ['json']15 }) or { return usage_error(err.msg()) }16 message := args.positional_at(0)17 if message == '' {18 example := if is_checkpoint {19 'before combine replay engine'20 } else {21 'fix off-by-one in chunk boundary'22 }23 return usage_error('${verb} needs a message, e.g. koh ${verb} "${example}"')24 }25 store := open_store() or { return refuse(args.json(), err.msg()) }26 defer {27 store.close()28 }29 mut hashing := reading()30 walked := snapshot.walk_report(store, store.root, snapshot.load(store.root), mut hashing) or {31 return refuse(args.json(), err.msg())32 }33 save := history.save_worktree(store, store.root, snapshot.load(store.root), history.SaveInput{34 message: message35 agent: args.value('agent')36 session: args.value('session')37 is_checkpoint: is_checkpoint38 storing: fn (done int, total int) {39 if done % 100 == 1 || done == total {40 working('storing ${done} / ${total} files')41 }42 }43 }) or { return refuse(args.json(), err.msg()) }4445 files := history.files_of(store, save.id) or { []snapshot.FileEntry{} }46 if args.json() {47 emit_json(json2.encode(SaveOut{48 id: save.id49 parent: save.parent50 message: save.message51 created_at: save.created_at52 machine: save.machine53 is_checkpoint: save.is_checkpoint54 agent: save.agent55 session: save.session56 files: files.len57 size_bytes: save.size_bytes58 unsafe_paths: walked.unsafe_paths59 }))60 auto_offer(store, true)61 return 062 }63 for path in walked.unsafe_paths {64 warn('not saved: unsafe name ${path}')65 }66 ok('saved [${save.id}] ${save.message}')67 mut detail := []string{}68 if save.is_checkpoint {69 detail << 'checkpoint'70 }71 detail << fmt.stamp(save.created_at)72 detail << fmt.plural(files.len, 'file')73 detail << fmt.human_bytes(save.size_bytes)74 note(detail.join(' · '))75 mut attribution := []string{}76 if save.agent != '' {77 attribution << 'agent: ${save.agent}'78 }79 if save.session != '' {80 attribution << 'session: ${save.session}'81 }82 if attribution.len > 0 {83 note(attribution.join(' · '))84 }85 auto_offer(store, false)86 return 087}88899091struct SaveOut {92 id string93 parent string94 message string95 created_at i6496 machine string97 is_checkpoint bool98 agent string99 session string100 files int101 size_bytes i64102 unsafe_paths []string103}