- On open, read legacy transcript files and write them to the new terminal-scoped history path - Remove legacy files after migration and warn if cleanup fails - Update tests to assert migration, content preservation, and legacy file removal
Unified · Split
| @@ -334,15 +334,18 @@ describe("TerminalManager", () => { · 333 unmodified lines | ||
| 334 | 334 | manager.dispose(); |
| 335 | 335 | }); |
| 336 | 336 | |
| 337 | − it("loads existing legacy transcript filenames and keeps new naming for writes", async () => { | |
| 337 | + it("migrates legacy transcript filenames to terminal-scoped history path on open", async () => { | |
| 338 | 338 | const { manager, logsDir } = makeManager(); |
| 339 | 339 | const legacyPath = path.join(logsDir, "thread-1.log"); |
| 340 | + const nextPath = historyLogPath(logsDir); | |
| 340 | 341 | fs.writeFileSync(legacyPath, "legacy-line\n", "utf8"); |
| 341 | 342 | |
| 342 | 343 | const snapshot = await manager.open(openInput()); |
| 343 | 344 | |
| 344 | 345 | expect(snapshot.history).toBe("legacy-line\n"); |
| 345 | − expect(fs.existsSync(legacyPath)).toBe(true); | |
| 346 | + expect(fs.existsSync(nextPath)).toBe(true); | |
| 347 | + expect(fs.readFileSync(nextPath, "utf8")).toBe("legacy-line\n"); | |
| 348 | + expect(fs.existsSync(legacyPath)).toBe(false); | |
| 346 | 349 | |
| 347 | 350 | manager.dispose(); |
| 348 | 351 | }); |
| @@ -582,12 +582,12 @@ export class TerminalManager extends EventEmitter<TerminalManagerEvents> { · 581 unmodified lines | ||
| 582 | 582 | } |
| 583 | 583 | |
| 584 | 584 | private async readHistory(threadId: string, terminalId: string): Promise<string> { |
| 585 | − const historyPath = this.historyPath(threadId, terminalId); | |
| 585 | + const nextPath = this.historyPath(threadId, terminalId); | |
| 586 | 586 | try { |
| 587 | − const raw = await fs.promises.readFile(historyPath, "utf8"); | |
| 587 | + const raw = await fs.promises.readFile(nextPath, "utf8"); | |
| 588 | 588 | const capped = capHistory(raw, this.historyLineLimit); |
| 589 | 589 | if (capped !== raw) { |
| 590 | − await fs.promises.writeFile(historyPath, capped, "utf8"); | |
| 590 | + await fs.promises.writeFile(nextPath, capped, "utf8"); | |
| 591 | 591 | } |
| 592 | 592 | return capped; |
| 593 | 593 | } catch (error) { |
| @@ -600,12 +600,22 @@ export class TerminalManager extends EventEmitter<TerminalManagerEvents> { · 6 unmodified lines | ||
| 600 | 600 | return ""; |
| 601 | 601 | } |
| 602 | 602 | |
| 603 | + const legacyPath = this.legacyHistoryPath(threadId); | |
| 603 | 604 | try { |
| 604 | − const raw = await fs.promises.readFile(this.legacyHistoryPath(threadId), "utf8"); | |
| 605 | + const raw = await fs.promises.readFile(legacyPath, "utf8"); | |
| 605 | 606 | const capped = capHistory(raw, this.historyLineLimit); |
| 606 | − if (capped !== raw) { | |
| 607 | − await fs.promises.writeFile(this.legacyHistoryPath(threadId), capped, "utf8"); | |
| 607 | + | |
| 608 | + // Migrate legacy transcript filename to the terminal-scoped path. | |
| 609 | + await fs.promises.writeFile(nextPath, capped, "utf8"); | |
| 610 | + try { | |
| 611 | + await fs.promises.rm(legacyPath, { force: true }); | |
| 612 | + } catch (cleanupError) { | |
| 613 | + this.logger.warn("failed to remove legacy terminal history", { | |
| 614 | + threadId, | |
| 615 | + error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError), | |
| 616 | + }); | |
| 608 | 617 | } |
| 618 | + | |
| 609 | 619 | return capped; |
| 610 | 620 | } catch (error) { |
| 611 | 621 | if ((error as NodeJS.ErrnoException).code === "ENOENT") { |