@@ -31,6 +31,7 @@ const { execSync, spawnSync } = require("node:child_process");
3131const os = require ( "node:os" ) ;
3232
3333const {
34+ buildBlobUrl,
3435 buildSnapshotRef,
3536 ensureBackupRepo,
3637 resolveGhEnvironment,
@@ -370,33 +371,66 @@ const buildManifest = ({ backupRepo, snapshotRef, source, files, createdAt }) =>
370371 files,
371372} ) ;
372373
373- const buildCommentBody = ( { backupRepo, source, manifestUrl, files } ) => {
374+ const formatBytes = ( bytes ) => {
375+ if ( bytes >= 1_000_000_000 ) {
376+ return `${ ( bytes / 1_000_000_000 ) . toFixed ( 2 ) } GB` ;
377+ }
378+ if ( bytes >= 1_000_000 ) {
379+ return `${ ( bytes / 1_000_000 ) . toFixed ( 2 ) } MB` ;
380+ }
381+ if ( bytes >= 1_000 ) {
382+ return `${ ( bytes / 1_000 ) . toFixed ( 2 ) } KB` ;
383+ }
384+ return `${ bytes } B` ;
385+ } ;
386+
387+ const summarizeFiles = ( files ) => ( {
388+ fileCount : files . length ,
389+ totalBytes : files . reduce (
390+ ( sum , file ) => sum + ( file . type === "chunked" ? ( file . originalSize ?? 0 ) : ( file . size ?? 0 ) ) ,
391+ 0
392+ ) ,
393+ } ) ;
394+
395+ const buildSnapshotReadme = ( { backupRepo, source, manifestUrl, summary, sessionRoots } ) =>
396+ [
397+ "# AI Session Backup" ,
398+ "" ,
399+ "This snapshot contains AI session data used during development." ,
400+ "" ,
401+ `- Backup Repo: \`${ backupRepo . fullName } \`` ,
402+ `- Source Repo: \`${ source . repo } \`` ,
403+ `- Source Branch: \`${ source . branch } \`` ,
404+ `- Source Commit: \`${ source . commitSha } \`` ,
405+ source . prNumber === null ? "- Pull Request: none" : `- Pull Request: #${ source . prNumber } ` ,
406+ `- Created At: \`${ source . createdAt } \`` ,
407+ `- Files: \`${ summary . fileCount } \`` ,
408+ `- Total Size: \`${ formatBytes ( summary . totalBytes ) } \`` ,
409+ `- Session Roots: \`${ sessionRoots . join ( "`, `" ) } \`` ,
410+ "" ,
411+ `- Manifest: ${ manifestUrl } ` ,
412+ "" ,
413+ "Generated automatically by the docker-git `pre-push` session backup hook." ,
414+ "" ,
415+ ] . join ( "\n" ) ;
416+
417+ const buildCommentBody = ( { backupRepo, source, manifestUrl, readmeUrl, summary } ) => {
374418 const lines = [
375419 "## AI Session Backup" ,
376420 "" ,
377- "A snapshot of the AI agent session has been saved to the private session backup repository ." ,
421+ "A snapshot of the AI session context used during development has been saved ." ,
378422 "" ,
379- `**Backup Repo:** ${ backupRepo . fullName } ` ,
380- `**Source Commit:** \`${ source . commitSha } \`` ,
423+ `Backup Repo: ${ backupRepo . fullName } ` ,
424+ `Source Commit: ${ source . commitSha } ` ,
425+ `Created At: ${ source . createdAt } ` ,
426+ `Files: ${ summary . fileCount } (${ formatBytes ( summary . totalBytes ) } )` ,
381427 "" ,
382- `**Manifest:** ${ manifestUrl } ` ,
428+ `README: ${ readmeUrl } ` ,
429+ `Manifest: ${ manifestUrl } ` ,
383430 "" ,
384- "**Files:** " ,
431+ "This snapshot metadata was used during development. " ,
385432 ] ;
386433
387- for ( const file of files ) {
388- if ( file . type === "chunked" ) {
389- lines . push ( `- ${ file . name } (chunked): ${ file . chunkManifestUrl } ` ) ;
390- } else {
391- lines . push ( `- ${ file . name } : ${ file . url } ` ) ;
392- }
393- }
394-
395- lines . push ( "" ) ;
396- lines . push ( "For extracting session dialogs, see: https://github.com/ProverCoderAI/context-doc" ) ;
397- lines . push ( "" ) ;
398- lines . push ( "---" ) ;
399- lines . push ( `*Backup created at: ${ source . createdAt } *` ) ;
400434 lines . push ( `<!-- docker-git-session-backup:${ source . commitSha } :${ source . createdAt } -->` ) ;
401435 return lines . join ( "\n" ) ;
402436} ;
@@ -513,6 +547,11 @@ const main = () => {
513547 commitSha,
514548 createdAt : snapshotCreatedAt ,
515549 } ;
550+ const summary = summarizeFiles ( prepared . manifestFiles ) ;
551+ const sessionRoots = sessionDirs . map ( ( dir ) => `~/${ dir . name } ` ) ;
552+ const manifestUrl = buildBlobUrl ( backupRepo . fullName , backupRepo . defaultBranch , `${ snapshotRef } /manifest.json` ) ;
553+ const readmeRepoPath = `${ snapshotRef } /README.md` ;
554+ const readmeUrl = buildBlobUrl ( backupRepo . fullName , backupRepo . defaultBranch , readmeRepoPath ) ;
516555
517556 const manifest = buildManifest ( {
518557 backupRepo,
@@ -521,16 +560,35 @@ const main = () => {
521560 files : prepared . manifestFiles ,
522561 createdAt : snapshotCreatedAt ,
523562 } ) ;
563+ const readmePath = path . join ( tmpDir , "README.md" ) ;
564+ fs . writeFileSync (
565+ readmePath ,
566+ buildSnapshotReadme ( {
567+ backupRepo,
568+ source,
569+ manifestUrl,
570+ summary,
571+ sessionRoots,
572+ } ) ,
573+ "utf8"
574+ ) ;
575+ const uploadEntries = [
576+ ...prepared . uploadEntries ,
577+ {
578+ repoPath : readmeRepoPath ,
579+ sourcePath : readmePath ,
580+ type : "readme" ,
581+ size : fs . statSync ( readmePath ) . size ,
582+ } ,
583+ ] ;
524584 if ( args . dryRun ) {
525585 console . log ( `[dry-run] Would upload snapshot to ${ backupRepo . fullName } :${ snapshotRef } ` ) ;
526- console . log ( `[dry-run] Would write ${ prepared . uploadEntries . length + 1 } file(s) including manifest.` ) ;
527- const manifestUrl = `https://github.com/${ backupRepo . fullName } /blob/${
528- encodeURIComponent ( backupRepo . defaultBranch )
529- } /${ snapshotRef . split ( "/" ) . map ( ( segment ) => encodeURIComponent ( segment ) ) . join ( "/" ) } /manifest.json`;
586+ console . log ( `[dry-run] Would write ${ uploadEntries . length + 1 } file(s) including README and manifest.` ) ;
587+ console . log ( `[dry-run] README URL: ${ readmeUrl } ` ) ;
530588 console . log ( `[dry-run] Manifest URL: ${ manifestUrl } ` ) ;
531589 if ( args . postComment && prContext !== null ) {
532590 console . log ( `[dry-run] Would post comment to PR #${ prContext . prNumber } in ${ prContext . repo } :` ) ;
533- console . log ( buildCommentBody ( { backupRepo, source, manifestUrl, files : prepared . manifestFiles } ) ) ;
591+ console . log ( buildCommentBody ( { backupRepo, source, manifestUrl, readmeUrl , summary } ) ) ;
534592 }
535593 return ;
536594 }
@@ -540,19 +598,21 @@ const main = () => {
540598 backupRepo ,
541599 snapshotRef ,
542600 manifest ,
543- prepared . uploadEntries ,
601+ uploadEntries ,
544602 ghEnv
545603 ) ;
546604
547605 console . log ( `[session-backup] Uploaded snapshot to ${ backupRepo . fullName } ` ) ;
606+ console . log ( `[session-backup] README: ${ readmeUrl } ` ) ;
548607 console . log ( `[session-backup] Manifest: ${ uploadResult . manifestUrl } ` ) ;
549608
550609 if ( args . postComment && prContext !== null ) {
551610 const comment = buildCommentBody ( {
552611 backupRepo,
553612 source,
554613 manifestUrl : uploadResult . manifestUrl ,
555- files : prepared . manifestFiles ,
614+ readmeUrl,
615+ summary,
556616 } ) ;
557617 postPrComment ( prContext . repo , prContext . prNumber , comment , verbose , ghEnv ) ;
558618 }
0 commit comments