Skip to content

Commit e2053aa

Browse files
committed
feat(session-backups): store snapshots in private repo
1 parent fee64b1 commit e2053aa

9 files changed

Lines changed: 892 additions & 476 deletions

File tree

.githooks/pre-push

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ fi
1111

1212
node scripts/pre-push-knowledge-guard.js "$@"
1313

14-
# CHANGE: backup AI session to private gist on push (supports Claude, Codex, Gemini)
15-
# WHY: allows returning to old AI sessions and provides PR context
14+
# CHANGE: backup AI session to a private session repository on push (supports Claude, Codex, Gemini)
15+
# WHY: allows returning to old AI sessions and provides PR context without gist limits
1616
# QUOTE(ТЗ): "когда происходит push мы сразу заливаем текущую сессию с AI агентом в gits приватный"
1717
# REF: issue-143
1818
# PURITY: SHELL
1919
if [ "${DOCKER_GIT_SKIP_SESSION_BACKUP:-}" != "1" ]; then
20-
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
20+
if command -v gh >/dev/null 2>&1; then
2121
node scripts/session-backup-gist.js --verbose || echo "[session-backup] Warning: session backup failed (non-fatal)"
2222
fi
2323
fi

packages/app/src/docker-git/cli/parser-session-gists.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {
1111

1212
import { parsePositiveInt, parseProjectDirWithOptions, splitSubcommand } from "./parser-shared.js"
1313

14-
// CHANGE: parse session gist commands for backup/list/view/download
15-
// WHY: enables CLI access to session backup gist functionality
14+
// CHANGE: parse session backup commands for backup/list/view/download
15+
// WHY: enables CLI access to session backup repository functionality
1616
// QUOTE(ТЗ): "иметь возможность возвращаться ко всем старым сессиям с агентами"
1717
// REF: issue-143
1818
// PURITY: CORE
@@ -23,11 +23,11 @@ import { parsePositiveInt, parseProjectDirWithOptions, splitSubcommand } from ".
2323
const defaultLimit = 20
2424
const defaultOutputDir = "./.session-restore"
2525

26-
const missingGistIdError: ParseError = { _tag: "MissingRequiredOption", option: "gist-id" }
26+
const missingSnapshotRefError: ParseError = { _tag: "MissingRequiredOption", option: "snapshot-ref" }
2727

28-
const extractGistId = (args: ReadonlyArray<string>): string | null => {
29-
const gistId = args[0]
30-
return gistId && !gistId.startsWith("-") ? gistId : null
28+
const extractSnapshotRef = (args: ReadonlyArray<string>): string | null => {
29+
const snapshotRef = args[0]
30+
return snapshotRef && !snapshotRef.startsWith("-") ? snapshotRef : null
3131
}
3232

3333
const parseBackup = (
@@ -59,22 +59,22 @@ const parseList = (
5959
const parseView = (
6060
args: ReadonlyArray<string>
6161
): Either.Either<SessionGistViewCommand, ParseError> => {
62-
const gistId = extractGistId(args)
63-
return gistId
64-
? Either.right({ _tag: "SessionGistView", gistId })
65-
: Either.left(missingGistIdError)
62+
const snapshotRef = extractSnapshotRef(args)
63+
return snapshotRef
64+
? Either.right({ _tag: "SessionGistView", snapshotRef })
65+
: Either.left(missingSnapshotRefError)
6666
}
6767

6868
const parseDownload = (
6969
args: ReadonlyArray<string>
7070
): Either.Either<SessionGistDownloadCommand, ParseError> => {
71-
const gistId = extractGistId(args)
72-
if (!gistId) {
73-
return Either.left(missingGistIdError)
71+
const snapshotRef = extractSnapshotRef(args)
72+
if (!snapshotRef) {
73+
return Either.left(missingSnapshotRefError)
7474
}
7575
return Either.map(parseProjectDirWithOptions(args.slice(1)), ({ raw }) => ({
7676
_tag: "SessionGistDownload",
77-
gistId,
77+
snapshotRef,
7878
outputDir: raw.output ?? defaultOutputDir
7979
}))
8080
}

packages/app/src/docker-git/cli/usage.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ docker-git sessions kill <pid> [<url>] [options]
1616
docker-git sessions logs <pid> [<url>] [options]
1717
docker-git session-gists [list] [options]
1818
docker-git session-gists backup [<url>] [options]
19-
docker-git session-gists view <gist-id>
20-
docker-git session-gists download <gist-id> [options]
19+
docker-git session-gists view <snapshot-ref>
20+
docker-git session-gists download <snapshot-ref> [options]
2121
docker-git ps
2222
docker-git down-all
2323
docker-git auth <provider> <action> [options]
@@ -34,7 +34,7 @@ Commands:
3434
panes, terms List tmux panes for a docker-git project
3535
scrap Export/import project scrap (session snapshot + rebuildable deps)
3636
sessions List/kill/log container terminal processes
37-
session-gists Manage AI session backups via GitHub Gists (backup/list/view/download)
37+
session-gists Manage AI session backups via a private session repository (backup/list/view/download)
3838
ps, status Show docker compose status for all docker-git projects
3939
down-all Stop all docker-git containers (docker compose down)
4040
auth Manage GitHub/Codex/Claude Code auth for docker-git
@@ -71,8 +71,8 @@ Options:
7171
--include-default Show default/system processes in sessions list
7272
--pr-number <n> PR number for session backup comment
7373
--repo <owner/repo> Repository for session backup operations
74-
--limit <n> Limit for session-gists list (default: 20)
75-
--output <path> Output directory for session-gists download (default: ./.session-restore)
74+
--limit <n> Limit for session backup snapshot list (default: 20)
75+
--output <path> Output directory for session backup download (default: ./.session-restore)
7676
--no-comment Skip posting PR comment after session backup
7777
--up | --no-up Run docker compose up after init (default: --up)
7878
--ssh | --no-ssh Auto-open SSH after create/clone (default: clone=--ssh, create=--no-ssh)

packages/lib/src/core/session-gist-domain.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// CHANGE: session gist backup commands for PR-based session history
2-
// WHY: enables returning to old AI sessions via private gists linked to PRs
1+
// CHANGE: session backup commands for PR-based session history
2+
// WHY: enables returning to old AI sessions via a private backup repository
33
// QUOTE(ТЗ): "иметь возможность возвращаться ко всем старым сессиям с агентами"
44
// REF: issue-143
55
// PURITY: CORE
@@ -20,12 +20,12 @@ export interface SessionGistListCommand {
2020

2121
export interface SessionGistViewCommand {
2222
readonly _tag: "SessionGistView"
23-
readonly gistId: string
23+
readonly snapshotRef: string
2424
}
2525

2626
export interface SessionGistDownloadCommand {
2727
readonly _tag: "SessionGistDownload"
28-
readonly gistId: string
28+
readonly snapshotRef: string
2929
readonly outputDir: string
3030
}
3131

packages/lib/src/core/templates-entrypoint/git.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
262262
cd "$REPO_ROOT"
263263
264264
if [ "${"${"}DOCKER_GIT_SKIP_SESSION_BACKUP:-}" != "1" ]; then
265-
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
265+
if command -v gh >/dev/null 2>&1; then
266266
node scripts/session-backup-gist.js --verbose || echo "[session-backup] Warning: session backup failed (non-fatal)"
267267
fi
268268
fi

packages/lib/src/usecases/session-gists.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
import { runCommandWithExitCodes } from "../shell/command-runner.js"
1212
import { CommandFailedError } from "../shell/errors.js"
1313

14-
// CHANGE: implement session gist operations via shell commands
14+
// CHANGE: implement session backup repository operations via shell commands
1515
// WHY: enables CLI access to session backup/list/view/download functionality
1616
// QUOTE(ТЗ): "иметь возможность возвращаться ко всем старым сессиям с агентами"
1717
// REF: issue-143
@@ -55,7 +55,7 @@ export const sessionGistBackup = (
5555
args.push("--no-comment")
5656
}
5757
return Effect.gen(function*(_) {
58-
yield* _(Effect.log("Backing up AI session to private gist..."))
58+
yield* _(Effect.log("Backing up AI session to private session repository..."))
5959
yield* _(runNodeScript("scripts/session-backup-gist.js", args))
6060
yield* _(Effect.log("Session backup complete."))
6161
})
@@ -69,7 +69,7 @@ export const sessionGistList = (
6969
args.push("--repo", cmd.repo)
7070
}
7171
return Effect.gen(function*(_) {
72-
yield* _(Effect.log("Listing session backup gists..."))
72+
yield* _(Effect.log("Listing session backup snapshots..."))
7373
yield* _(runNodeScript("scripts/session-list-gists.js", args))
7474
})
7575
}
@@ -78,15 +78,15 @@ export const sessionGistView = (
7878
cmd: SessionGistViewCommand
7979
): Effect.Effect<void, SessionGistsError, SessionGistsRequirements> =>
8080
Effect.gen(function*(_) {
81-
yield* _(Effect.log(`Viewing gist: ${cmd.gistId}`))
82-
yield* _(runNodeScript("scripts/session-list-gists.js", ["view", cmd.gistId]))
81+
yield* _(Effect.log(`Viewing snapshot: ${cmd.snapshotRef}`))
82+
yield* _(runNodeScript("scripts/session-list-gists.js", ["view", cmd.snapshotRef]))
8383
})
8484

8585
export const sessionGistDownload = (
8686
cmd: SessionGistDownloadCommand
8787
): Effect.Effect<void, SessionGistsError, SessionGistsRequirements> =>
8888
Effect.gen(function*(_) {
89-
yield* _(Effect.log(`Downloading gist ${cmd.gistId} to ${cmd.outputDir}...`))
90-
yield* _(runNodeScript("scripts/session-list-gists.js", ["download", cmd.gistId, "--output", cmd.outputDir]))
89+
yield* _(Effect.log(`Downloading snapshot ${cmd.snapshotRef} to ${cmd.outputDir}...`))
90+
yield* _(runNodeScript("scripts/session-list-gists.js", ["download", cmd.snapshotRef, "--output", cmd.outputDir]))
9191
yield* _(Effect.log("Download complete."))
9292
})

0 commit comments

Comments
 (0)