cmd_save.v

2.4 KB · raw

1//! `koh save` and `koh checkpoint`.23module cli45import json6import 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	if snapshot.restore_pending(store) {30		return refuse(args.json(), snapshot.restore_unfinished)31	}32	save := history.commit_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(json.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		}))53		auto_offer(store, true)54		return 055	}56	ok('saved [${save.id}]  ${save.message}')57	mut detail := []string{}58	if save.is_checkpoint {59		detail << 'checkpoint'60	}61	detail << fmt.stamp(save.created_at)62	detail << fmt.plural(files.len, 'file')63	detail << fmt.human_bytes(save.size_bytes)64	note(detail.join(' · '))65	mut attribution := []string{}66	if save.agent != '' {67		attribution << 'agent: ${save.agent}'68	}69	if save.session != '' {70		attribution << 'session: ${save.session}'71	}72	if attribution.len > 0 {73		note(attribution.join(' · '))74	}75	auto_offer(store, false)76	return 077}7879// Every field is always present, so a consumer never has to tell "absent" from80// "missing key". An empty string means the save recorded none.81struct SaveOut {82	id            string83	parent        string84	message       string85	created_at    i6486	machine       string87	is_checkpoint bool88	agent         string89	session       string90	files         int91	size_bytes    i6492}