feat(git): a Fetch button in the Git status header, and an interval for it app-wide and per case

Fetch is the only git command Codeman runs that writes, and the only one that runs a
repository's own fetch-post hook, so it stays behind a click and off by default. The Git
status window's header row gains Fetch; App Settings -> Bottom bar gains the switch and
the interval, synced because the scheduler acts on them, and Session Options -> Git
overrides both per case. The scheduler reads settings fresh each tick, times the interval
from the start of a run, never overlaps runs, and re-checks the Docker and remote rules
every tick before any git runs. Nothing but remote-tracking refs moves: HEAD, the index
and the working tree are proven unchanged against real git while origin/main advances.

Tests: src/git-fetch.ts and the scheduler, the per-case settings route, the synced/display
setting split, the two new i18n rows, and the Fetch block of the Git status browser suite.
This commit is contained in:
Devvyn committed 2026-10-25 17:23:55 +08:00
1 parent 3e06f3c141
commit 9432d9f9e2
35 files changed
+2340 -39

No files matched your search

+11
View File
@@ -33,6 +33,7 @@
import type { RespawnConfig } from './respawn.js';
import type { AttachmentDetectedType } from './tools.js';
import type { GitFetchStatus } from '../git-fetch.js';
/** Status of a Claude session */
export type SessionStatus = 'idle' | 'busy' | 'stopped' | 'error';
@@ -891,6 +892,16 @@ export interface SessionState {
keepWarmEnabled?: boolean;
/** What the keep-warm is doing right now; ephemeral, never restored. */
keepWarm?: KeepWarmStatus;
/**
* Git fetch on an interval for this case's repositories; absent = follow the app-wide
* `gitFetchEnabled` default. Restored on a restart, like `keepWarmEnabled`, because the choice is the
* user's and outlives the run.
*/
gitFetchEnabled?: boolean;
/** Seconds between this case's runs; absent = follow the app-wide `gitFetchIntervalSeconds` default. */
gitFetchIntervalSeconds?: number;
/** What this case's last fetch did; ephemeral, never restored. */
gitFetch?: GitFetchStatus;
/** Pinned to the top of the session manager list (COD-139) */
pinned?: boolean;
/** When the session was pinned (epoch ms) — orders the pinned group, most-recent-first */