Documentation
¶
Overview ¶
Package externalcli implements docs/multi-device-storage.md §5.5.1's continuity layer for path-hashed external-CLI transcript stores (claude, codex).
Forward (`--migrate-external-cli`): when v0 → v1 changes the agent working directory from <v0>/agents/<id> to <v1>/agents/<id>, the external CLIs would otherwise lose their prior chat history. We create a symlink (or, on Windows, a junction) at the v1-hash directory pointing at the v0-hash directory so each CLI's internal path-hash lookup still resolves to the same on-disk state.
Reverse (`--rollback-external-cli`): every forward operation is recorded into a manifest under the v1 dir. Rollback walks the manifest in reverse and undoes each entry. Importantly the v0-hash target is NEVER touched — symlink removal uses os.Remove (not os.RemoveAll) so the underlying transcript store stays intact for an operator who is rolling back to v0.
All forward operations are best-effort: any individual failure is logged as a warning and the migration continues. The v1 install continues to work; the affected agent simply starts with a fresh chat session.
Index ¶
Constants ¶
const ManifestFileName = "external_cli_migration.json"
ManifestFileName is the on-disk record of forward operations, written under the v1 dir. Its presence is the trigger that `kojo --rollback-external-cli` looks for.
Variables ¶
This section is empty.
Functions ¶
func Rollback ¶
Rollback walks the manifest in reverse and undoes each operation. Errors are returned as warnings — rollback is best-effort by design; an entry the operator already cleaned up by hand should not block the remaining entries.
func SaveManifest ¶
SaveManifest writes the manifest atomically. An empty Ops slice still writes the file so a subsequent rollback knows the forward pass ran (vs "we forgot to record anything").
Types ¶
type CLISpec ¶
CLISpec describes one path-hash CLI's transcript layout for the forward pass. ProjectsRoot is the directory under which each CLI keeps a per-path subdir (e.g. ~/.claude/projects/).
type Hasher ¶
Hasher is the path-hash function used by claude / codex. The forward pass reuses internal/agent's claudeEncodePath; tests can substitute a stub.
type Manifest ¶
Manifest is the JSON file written under the v1 dir.
func ApplyForward ¶
ApplyForward executes the planned operations and records each successful one into the returned Manifest. Failures are turned into warnings (returned as the second value); a partial manifest is still returned so rollback can undo the operations that did succeed.
func LoadManifest ¶
LoadManifest reads the manifest from v1Dir. Returns (nil, nil) when the file is absent — rollback then has nothing to undo.
type Op ¶
type Op struct {
Kind OpKind `json:"kind"`
Path string `json:"path"`
Target string `json:"target,omitempty"`
// AgentID is informational — included to help an operator who is
// auditing the manifest understand why each entry exists.
AgentID string `json:"agent_id,omitempty"`
}
Op is one reversible forward action recorded for rollback.
func PlanSymlinks ¶
PlanSymlinks builds the list of OpSymlink entries for the given CLIs and agents. Skips any (cli, agent) pair where:
- the v0 hash directory does not exist (CLI never ran for that agent in v0; nothing to alias)
- the v1 hash directory already exists (collision; do not clobber)
PlanSymlinks does NOT touch the filesystem; it only computes what Apply would do. Used by tests to assert plan correctness.
type OpKind ¶
type OpKind string
OpKind enumerates reversible operations we know how to perform.
const ( // OpSymlink: a path-hash directory symlink was created. Path = // the new (v1) symlink. Target = the v0 hash directory it points // at. Reverse with os.Remove(Path) (do not touch Target). OpSymlink OpKind = "symlink" )