a1069211faa5136f7038b3c31e715503fcecd413

Author
TheEdgeOfRage <git@theedgeofrage.com>
Committer
TheEdgeOfRage <git@theedgeofrage.com>
Date

Message

Remove backup functionality

Diff

This diff is truncated to protect this page.

  1diff --git a/README.md b/README.md
  2index f827688dcd2a63183994ec9c5975ff0e8c1e0376..32c0c88dca476c7ae20efe50c3253a8803b64c7c 100644
  3--- a/README.md
  4+++ b/README.md
  5@@ -1,6 +1,6 @@
  6 # Pi Tree Pruning
  7 
  8-Pi extension that removes inactive branches from an on-disk session tree. It creates a backup before it changes the session. Use `/tree-prune undo` to restore a backup.
  9+Pi extension that permanently removes inactive branches from an on-disk session tree.
 10 
 11 ## Install
 12 
 13diff --git a/index.ts b/index.ts
 14index ad3aff71dd5fa0e23b191e6b8716c387aac0e5db..e3c24944cbf7beefa2d7939881813b3c87bf30da 100644
 15--- a/index.ts
 16+++ b/index.ts
 17@@ -1,4 +1,3 @@
 18-import { basename } from "node:path";
 19 import type {
 20   ExtensionAPI,
 21   ExtensionCommandContext,
 22@@ -8,10 +7,7 @@ import {
 23   captureSessionFile,
 24   countPruneEntries,
 25   getSessionTreeEntries,
 26-  createBackupPath,
 27-  listSessionBackups,
 28   pruneSessionFile,
 29-  restoreSessionBackup,
 30   toSessionFilePath,
 31   type SessionFilePath,
 32 } from "./session-file.ts";
 33@@ -32,7 +28,7 @@ const COMMAND_NAME = "tree-prune";
 34 
 35 export default function registerTreePruning(pi: ExtensionAPI): void {
 36   pi.registerCommand(COMMAND_NAME, {
 37-    description: "Delete inactive session-tree branches with a backup; use /tree-prune undo to restore",
 38+    description: "Delete inactive session-tree branches",
 39     handler: runTreePruneCommand,
 40   });
 41 }
 42@@ -53,12 +49,7 @@ export async function runTreePruneCommand(
 43     return;
 44   }
 45 
 46-  if (mode === "undo") {
 47-    await runUndo(ctx);
 48-    return;
 49-  }
 50-
 51-  ctx.ui.notify("Usage: /tree-prune [undo]", "error");
 52+  ctx.ui.notify("Usage: /tree-prune", "error");
 53 }
 54 
 55 async function runPrune(ctx: ExtensionCommandContext): Promise<void> {
 56@@ -96,30 +87,28 @@ async function runPrune(ctx: ExtensionCommandContext): Promise<void> {
 57       const item = treeItems.find((treeItem) => treeItem.candidate.rootId === candidate.rootId);
 58       return `• ${item ? formatPruneTreeItem(item) : candidate.preview}`;
 59     });
 60-    const backupPath = createBackupPath(sessionFile);
 61     const confirmed = await ctx.ui.confirm(
 62       "Delete inactive branches?",
 63       [
 64         "Branches to delete:",
 65         ...branchesToPrune,
 66         `Entries to delete: ${formatEntryCount(removedEntryCount)}`,
 67-        `Backup: ${backupPath}`,
 68-        "The branches will be removed from this session file.",
 69+        "The branches will be permanently removed from this session file.",
 70       ].join("\n"),
 71     );
 72     if (!confirmed) return;
 73 
 74-    const result = await pruneSessionFile(snapshot, candidatesToPrune, backupPath);
 75+    await pruneSessionFile(snapshot, candidatesToPrune);
 76     const switchResult = await reloadSession(
 77       ctx,
 78       sessionFile,
 79       activeLeafId,
 80-      `Tree Prune removed ${formatEntryCount(result.removedEntryCount)}. Backup: ${result.backupPath}`,
 81+      `Tree Prune removed ${formatEntryCount(removedEntryCount)}.`,
 82     );
 83 
 84     if (switchResult.cancelled) {
 85       ctx.ui.notify(
 86-        `Tree Prune changed the session, but reload was cancelled. Resume ${sessionFile} to load it. Backup: ${result.backupPath}`,
 87+        `Tree Prune changed the session, but reload was cancelled. Resume ${sessionFile} to load it.`,
 88         "error",
 89       );
 90     }
 91@@ -128,67 +117,6 @@ async function runPrune(ctx: ExtensionCommandContext): Promise<void> {
 92   }
 93 }
 94 
 95-async function runUndo(ctx: ExtensionCommandContext): Promise<void> {
 96-  try {
 97-    await ctx.waitForIdle();
 98-
 99-    const sessionFile = requireSessionFile(ctx);
100-    const currentLeafId = ctx.sessionManager.getLeafId();
101-    const activeLeafId = currentLeafId ? toEntryId(currentLeafId) : undefined;
102-    const currentSnapshot = await captureSessionFile(sessionFile);
103-    const backups = await listSessionBackups(sessionFile);
104-    if (backups.length === 0) {
105-      ctx.ui.notify("Tree Prune found no backups for this session", "info");
106-      return;
107-    }
108-
109-    const selectedBackupPath = await ctx.ui.select(
110-      "Restore Tree Prune backup:",
111-      backups.map((backup) => basename(backup)),
112-    );
113-    if (!selectedBackupPath) return;
114-
115-    const selectedBackup = backups.find((backup) => basename(backup) === selectedBackupPath);
116-    if (!selectedBackup) {
117diff --git a/session-file.ts b/session-file.ts
118index e665d251c1823e08cdffdf57b5b84ced6ec7bbe4..99d33c85fe79fa4a3f8234ee4a852aa23e432b29 100644
119--- a/session-file.ts
120+++ b/session-file.ts
121@@ -1,5 +1,5 @@
122 import { randomUUID } from "node:crypto";
123-import { chmod, link, lstat, open, readdir, readFile, rename, unlink } from "node:fs/promises";
124+import { chmod, lstat, open, readFile, rename, unlink } from "node:fs/promises";
125 import { basename, dirname, join, resolve } from "node:path";
126 import {
127   toEntryId,
128@@ -117,27 +117,6 @@ export function buildPrunedSessionContent(
129     .join("");
130 }
131 
132-export function createBackupPath(sessionFile: SessionFilePath): SessionFilePath {
133-  const path = toSessionFilePath(sessionFile);
134-  const timestamp = new Date().toISOString().replaceAll(":", "-").replaceAll(".", "-");
135-  return toSessionFilePath(
136-    join(dirname(path), `${basename(path)}.tree-prune-backup-${timestamp}-${randomUUID()}`),
137-  );
138-}
139-
140-export async function createSessionBackup(
141-  snapshot: SessionFileSnapshot,
142-  backupPath = createBackupPath(snapshot.path),
143-): Promise<SessionFilePath> {
144-  const path = toSessionFilePath(backupPath);
145-  if (dirname(path) !== dirname(snapshot.path)) {
146-    throw new Error("Backup must be in the session file directory");
147-  }
148-
149-  await writeNewFileAtomically(path, snapshot.content, snapshot.identity.mode);
150-  return path;
151-}
152-
153 export function countPruneEntries(candidates: readonly BranchCandidate[]): number {
154   return collectPruneEntries(candidates).size;
155 }
156@@ -145,44 +124,9 @@ export function countPruneEntries(candidates: readonly BranchCandidate[]): numbe
157 export async function pruneSessionFile(
158   snapshot: SessionFileSnapshot,
159   candidates: readonly BranchCandidate[],
160-  backupPath = createBackupPath(snapshot.path),
161-): Promise<{ backupPath: SessionFilePath; removedEntryCount: number }> {
162-  const removedEntryCount = countPruneEntries(candidates);
163+): Promise<void> {
164   const prunedContent = buildPrunedSessionContent(snapshot, candidates);
165-  await assertSessionFileUnchanged(snapshot);
166-  const createdBackupPath = await createSessionBackup(snapshot, backupPath);
167   await replaceSessionFile(snapshot, prunedContent);
168-
169-  return { backupPath: createdBackupPath, removedEntryCount };
170-}
171-
172-export async function restoreSessionBackup(
173-  currentSnapshot: SessionFileSnapshot,
174-  selectedBackup: SessionFileSnapshot,
175-  backupPath = createBackupPath(currentSnapshot.path),
176-): Promise<SessionFilePath> {
177-  if (dirname(selectedBackup.path) !== dirname(currentSnapshot.path)) {
178-    throw new Error("Backup must be in the session file directory");
179-  }
180-
181-  await assertSessionFileUnchanged(currentSnapshot);
182-  await assertSessionFileUnchanged(selectedBackup);
183-  const currentBackupPath = await createSessionBackup(currentSnapshot, backupPath);
184-  await replaceSessionFile(currentSnapshot, selectedBackup.content);
185-
186-  return currentBackupPath;
187-}
188-
189-export async function listSessionBackups(sessionFile: SessionFilePath): Promise<SessionFilePath[]> {
190-  const path = toSessionFilePath(sessionFile);
191-  const directory = dirname(path);
192-  const prefix = `${basename(path)}.tree-prune-backup-`;
193-  const directoryEntries = await readdir(directory, { withFileTypes: true });
194-
195-  return directoryEntries
196-    .filter((entry) => entry.isFile() && entry.name.startsWith(prefix))
197-    .map((entry) => toSessionFilePath(join(directory, entry.name)))
198-    .sort((left, right) => right.localeCompare(left));
199 }
200 
201 async function replaceSessionFile(snapshot: SessionFileSnapshot, content: string): Promise<void> {
202@@ -198,17 +142,6 @@ async function replaceSessionFile(snapshot: SessionFileSnapshot, content: string
203   }
204 }
205 
206-async function writeNewFileAtomically(path: string, content: string, mode: number): Promise<void> {
207-  const temporaryPath = temporaryPathFor(path);
208-
209-  try {
210-    await writeFileAndSync(temporaryPath, content, mode);
211-    await link(temporaryPath, path);
212-  } finally {
213-    await removeIfPresent(temporaryPath);
214-  }
215-}
216-
217 async function writeFileAndSync(path: string, content: string, mode: number): Promise<void> {
218   const file = await open(path, "wx", mode);
219   try {