git-sync: sync ~/.codex history repo across machines

Mirror the claude-history setup for Codex: ~/.codex is now a git repo
(github.com/colonelpanic8/codex-history) holding session rollouts and
history.jsonl from all machines, synced by git-sync-rs in watch mode.

- Generalize the nixos host gate (claudeHistoryHosts -> aiHistoryHosts)
  and add codex-history alongside claude-history on both NixOS and darwin.
- Drop the old HM-managed dotfiles/codex/.gitignore; the repo ships its
  own real .gitignore (git won't read a symlinked one).
- Shield dotfiles/codex/* in the root .gitignore so nested codex history
  can't be committed into dotfiles on darwin (where ~/.codex resolves
  into dotfiles/codex).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 01:45:40 -07:00
parent 994291b969
commit f3649945cb
4 changed files with 31 additions and 15 deletions

8
.gitignore vendored
View File

@@ -61,3 +61,11 @@ gotools
!/dotfiles/claude/settings.json !/dotfiles/claude/settings.json
!/dotfiles/claude/settings.local.json !/dotfiles/claude/settings.local.json
!/dotfiles/claude/settings.local.json.example !/dotfiles/claude/settings.local.json.example
# Same story for Codex: ~/.codex resolves into dotfiles/codex on nix-darwin,
# so the codex-history repo and live Codex state nest inside this worktree.
# Allowlist only the HM-managed config.
/dotfiles/codex/*
!/dotfiles/codex/AGENTS.md
!/dotfiles/codex/config.toml
!/dotfiles/codex/skills

View File

@@ -1,8 +0,0 @@
*
!.gitignore
!AGENTS.md
!config.toml
!skills
# Legacy generated/local Codex state under this repo stays ignored. Active
# host-local Codex fragments now live under ~/.codex.

View File

@@ -11,6 +11,7 @@
orgPath = "${config.home.homeDirectory}/org"; orgPath = "${config.home.homeDirectory}/org";
passwordStorePath = "${config.home.homeDirectory}/.password-store"; passwordStorePath = "${config.home.homeDirectory}/.password-store";
claudePath = "${config.home.homeDirectory}/.claude"; claudePath = "${config.home.homeDirectory}/.claude";
codexPath = "${config.home.homeDirectory}/.codex";
in { in {
services.git-sync = { services.git-sync = {
enable = true; enable = true;
@@ -30,6 +31,11 @@ in {
uri = "git@github.com:colonelpanic8/claude-history.git"; uri = "git@github.com:colonelpanic8/claude-history.git";
interval = 600; interval = 600;
}; };
codex-history = {
path = codexPath;
uri = "git@github.com:colonelpanic8/codex-history.git";
interval = 600;
};
}; };
}; };
@@ -43,5 +49,7 @@ in {
# untracked session files and throttle event-driven syncs. # untracked session files and throttle event-driven syncs.
git-sync-claude-history.config.ProgramArguments = git-sync-claude-history.config.ProgramArguments =
lib.mkForce ["${gitSyncPackage}/bin/git-sync-rs" "-d" claudePath "watch" "--new-files" "true" "--min-interval" "300"]; lib.mkForce ["${gitSyncPackage}/bin/git-sync-rs" "-d" claudePath "watch" "--new-files" "true" "--min-interval" "300"];
git-sync-codex-history.config.ProgramArguments =
lib.mkForce ["${gitSyncPackage}/bin/git-sync-rs" "-d" codexPath "watch" "--new-files" "true" "--min-interval" "300"];
}; };
} }

View File

@@ -5,11 +5,11 @@
... ...
}: let }: let
gitSyncServicePath = lib.makeBinPath [pkgs.coreutils pkgs.git pkgs.openssh]; gitSyncServicePath = lib.makeBinPath [pkgs.coreutils pkgs.git pkgs.openssh];
# ~/.claude history sync is being rolled out machine-by-machine; each new # AI chat-history sync (Claude Code + Codex) is rolled out machine-by-machine;
# machine needs its existing history merged into the repo first (see # each new machine needs its existing history merged into the repo first (see
# github.com/colonelpanic8/claude-history). # github.com/colonelpanic8/claude-history and .../codex-history).
claudeHistoryHosts = ["ryzen-shine" "railbird-sf" "jay-lenovo" "strixi-minaj"]; aiHistoryHosts = ["ryzen-shine" "railbird-sf" "jay-lenovo" "strixi-minaj"];
syncClaudeHistory = builtins.elem config.networking.hostName claudeHistoryHosts; syncAiHistory = builtins.elem config.networking.hostName aiHistoryHosts;
mkGitSyncTrayOverrides = icon: { mkGitSyncTrayOverrides = icon: {
Service = { Service = {
Environment = lib.mkMerge [ Environment = lib.mkMerge [
@@ -40,12 +40,17 @@ in {
uri = "git@github.com:IvanMalison/.password-store.git"; uri = "git@github.com:IvanMalison/.password-store.git";
}; };
} }
// lib.optionalAttrs syncClaudeHistory { // lib.optionalAttrs syncAiHistory {
claude-history = { claude-history = {
path = config.home.homeDirectory + "/.claude"; path = config.home.homeDirectory + "/.claude";
uri = "git@github.com:colonelpanic8/claude-history.git"; uri = "git@github.com:colonelpanic8/claude-history.git";
interval = 600; interval = 600;
}; };
codex-history = {
path = config.home.homeDirectory + "/.codex";
uri = "git@github.com:colonelpanic8/codex-history.git";
interval = 600;
};
}; };
}; };
@@ -55,13 +60,16 @@ in {
lib.nameValuePair "git-sync-${name}" lib.nameValuePair "git-sync-${name}"
(mkGitSyncTrayOverrides (repoIcons.${name} or "git"))) (mkGitSyncTrayOverrides (repoIcons.${name} or "git")))
config.services.git-sync.repositories) config.services.git-sync.repositories)
(lib.optionalAttrs syncClaudeHistory { (lib.optionalAttrs syncAiHistory {
# Live sessions append to their transcript on every message; sync # Live sessions append to their transcript on every message; sync
# untracked session files and throttle event-driven syncs so an # untracked session files and throttle event-driven syncs so an
# active session doesn't push once per append. # active session doesn't push once per append.
git-sync-claude-history.Service.ExecStart = git-sync-claude-history.Service.ExecStart =
lib.mkForce lib.mkForce
"${pkgs.git-sync-rs}/bin/git-sync-rs watch --new-files true --min-interval 300"; "${pkgs.git-sync-rs}/bin/git-sync-rs watch --new-files true --min-interval 300";
git-sync-codex-history.Service.ExecStart =
lib.mkForce
"${pkgs.git-sync-rs}/bin/git-sync-rs watch --new-files true --min-interval 300";
}) })
]; ];
}; };