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 walked := snapshot.walk_report(store, store.root, snapshot.load(store.root)) or {30 return refuse(args.json(), err.msg())31 }32 save := history.save_worktree(store, store.root, snapshot.load(store.root), history.SaveInput{33 message: message34 agent: args.value('agent')35 session: args.value('session')36 is_checkpoint: is_checkpoint37 }) or { return refuse(args.json(), err.msg()) }3839 files := history.files_of(store, save.id) or { []snapshot.FileEntry{} }40 if args.json() {41 emit_json(json2.encode(SaveOut{42 id: save.id43 parent: save.parent44 message: save.message45 created_at: save.created_at46 machine: save.machine47 is_checkpoint: save.is_checkpoint48 agent: save.agent49 session: save.session50 files: files.len51 size_bytes: save.size_bytes52 unsafe_paths: walked.unsafe_paths53 }))54 auto_offer(store, true)55 return 056 }57 for path in walked.unsafe_paths {58 warn('not saved: unsafe name ${path}')59 }60 ok('saved [${save.id}] ${save.message}')61 mut detail := []string{}62 if save.is_checkpoint {63 detail << 'checkpoint'64 }65 detail << fmt.stamp(save.created_at)66 detail << fmt.plural(files.len, 'file')67 detail << fmt.human_bytes(save.size_bytes)68 note(detail.join(' · '))69 mut attribution := []string{}70 if save.agent != '' {71 attribution << 'agent: ${save.agent}'72 }73 if save.session != '' {74 attribution << 'session: ${save.session}'75 }76 if attribution.len > 0 {77 note(attribution.join(' · '))78 }79 auto_offer(store, false)80 return 081}82838485struct SaveOut {86 id string87 parent string88 message string89 created_at i6490 machine string91 is_checkpoint bool92 agent string93 session string94 files int95 size_bytes i6496 unsafe_paths []string97}