Skip to content

Commit 4d25bea

Browse files
Myoontyeeclaude
andcommitted
feat: add Codex tidy support — remove stale environment_context titles
After fixing Codex title extraction (v0.6.8), old exported files still have "environment_context-cwd..." in their filenames. Tidy now cleans these up automatically. Changes: - exporter.ts: add tidyCodexHistory() — removes stale environment_context titled files when a correctly-titled file for the same shortId exists - extension.ts: tidyHistory command now also calls tidyCodexHistory() - extension.ts: Codex menu gets its own "Tidy .codex-history/" option Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 82af403 commit 4d25bea

File tree

3 files changed

+96
-5
lines changed

3 files changed

+96
-5
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "claude-code-exporter",
33
"displayName": "Claude Code Exporter — Claude Code, Codex & Cursor Conversation History",
44
"description": "Export AI coding agent conversations to Markdown. Supports Claude Code (→ .claude-code-history/), OpenAI Codex (→ .codex-history/), and Cursor Composer (→ .cursor-history/). Features: auto-watch, session inject/import for claude --resume, and repair of thinking-block signature errors when switching API providers.",
5-
"version": "0.6.8",
5+
"version": "0.6.9",
66
"publisher": "myoontyee",
77
"engines": {
88
"vscode": "^1.85.0"

src/exporter.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,73 @@ export class MarkdownExporter {
476476

477477
return { renamed, merged, errors, migrated };
478478
}
479+
480+
// ─── Tidy Codex History ───────────────────────────────────────────────────
481+
482+
/**
483+
* Tidy up .codex-history/ folder:
484+
* 1. Merge duplicates with same shortId (keep largest)
485+
* 2. Remove stale files with bad title "environment_context-cwd..." if a
486+
* correctly-titled file for the same shortId already exists
487+
*
488+
* Returns { merged, errors } counts.
489+
*/
490+
tidyCodexHistory(
491+
codexHistoryDir: string
492+
): { merged: number; errors: number } {
493+
if (!fs.existsSync(codexHistoryDir)) return { merged: 0, errors: 0 };
494+
495+
let merged = 0;
496+
let errors = 0;
497+
498+
const files = fs.readdirSync(codexHistoryDir).filter((f) => f.endsWith('.md'));
499+
const shortIdPattern = /_([a-f0-9]{8,})(?:_compact)?\.md$/;
500+
501+
// Group by shortId
502+
const byShortId = new Map<string, string[]>();
503+
for (const f of files) {
504+
const m = f.match(shortIdPattern);
505+
if (!m) continue;
506+
const sid = m[1];
507+
if (!byShortId.has(sid)) byShortId.set(sid, []);
508+
byShortId.get(sid)!.push(f);
509+
}
510+
511+
for (const [, group] of byShortId) {
512+
if (group.length <= 1) continue;
513+
514+
// Prefer file WITHOUT environment_context in the name (correct title)
515+
// Keep it; delete stale environment_context-titled ones
516+
const correct = group.filter((f) => !f.includes('environment_context'));
517+
const stale = group.filter((f) => f.includes('environment_context'));
518+
519+
if (correct.length > 0 && stale.length > 0) {
520+
// Have a correctly-titled file — delete all stale ones
521+
for (const f of stale) {
522+
try {
523+
fs.unlinkSync(path.join(codexHistoryDir, f));
524+
merged++;
525+
} catch { errors++; }
526+
}
527+
} else {
528+
// All are stale or all are correct — keep largest, delete rest
529+
group.sort((a, b) => {
530+
try {
531+
return fs.statSync(path.join(codexHistoryDir, b)).size -
532+
fs.statSync(path.join(codexHistoryDir, a)).size;
533+
} catch { return 0; }
534+
});
535+
for (let i = 1; i < group.length; i++) {
536+
try {
537+
fs.unlinkSync(path.join(codexHistoryDir, group[i]));
538+
merged++;
539+
} catch { errors++; }
540+
}
541+
}
542+
}
543+
544+
return { merged, errors };
545+
}
479546
}
480547

481548
// ─── Helpers ──────────────────────────────────────────────────────────────────

src/extension.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,21 @@ export function activate(context: vscode.ExtensionContext): void {
200200
return;
201201
}
202202
const ccHistory = path.join(workspaceRoot, '.claude-code-history');
203-
const result = exporter.tidyHistory(ccHistory, claudeProjectDir || undefined);
203+
const codexHistory = path.join(workspaceRoot, '.codex-history');
204+
205+
const ccResult = exporter.tidyHistory(ccHistory, claudeProjectDir || undefined);
206+
const codexResult = exporter.tidyCodexHistory(codexHistory);
207+
208+
const total = ccResult.merged + codexResult.merged;
209+
const totalErrors = ccResult.errors + codexResult.errors;
210+
204211
treeProvider.refresh();
205212
vscode.window.showInformationMessage(
206-
`Tidy done: ${result.merged} duplicates merged, ${result.renamed} files renamed` +
207-
(result.migrated > 0 ? `, ${result.migrated} file(s) migrated from .cc-history/` : '') +
208-
(result.errors > 0 ? `, ${result.errors} errors` : '')
213+
`Tidy done: ${total} duplicates merged` +
214+
(ccResult.renamed > 0 ? `, ${ccResult.renamed} files renamed` : '') +
215+
(ccResult.migrated > 0 ? `, ${ccResult.migrated} file(s) migrated from .cc-history/` : '') +
216+
(codexResult.merged > 0 ? ` (incl. ${codexResult.merged} stale Codex titles removed)` : '') +
217+
(totalErrors > 0 ? `, ${totalErrors} errors` : '')
209218
);
210219
}),
211220

@@ -581,6 +590,11 @@ async function handleCodexMenu(
581590
label: '$(folder-opened) Open .codex-history/ Folder',
582591
value: 'open',
583592
},
593+
{
594+
label: '$(tools) Tidy .codex-history/',
595+
description: 'Remove stale environment_context titles & merge duplicates',
596+
value: 'tidy',
597+
},
584598
], { placeHolder: 'Codex CLI Sessions' });
585599

586600
if (!choice) return;
@@ -590,6 +604,16 @@ async function handleCodexMenu(
590604
return;
591605
}
592606

607+
if (choice.value === 'tidy') {
608+
const codexHistory = path.join(workspaceRoot, '.codex-history');
609+
const result = exporter.tidyCodexHistory(codexHistory);
610+
vscode.window.showInformationMessage(
611+
`Codex Tidy done: ${result.merged} stale file(s) removed` +
612+
(result.errors > 0 ? `, ${result.errors} errors` : '')
613+
);
614+
return;
615+
}
616+
593617
if (choice.value === 'format') {
594618
const fmt = await pickFormat();
595619
if (!fmt) return;

0 commit comments

Comments
 (0)