jp_lit_prune_cache
Identifies outdated local cache entries and removes them when dry-run is disabled.
Instructions
古いローカルキャッシュ候補を列挙し、dry_run=false のときだけ安全に削除する
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| older_than_days | No | ||
| tool | No | ||
| dry_run | No | ||
| limit | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| dry_run | Yes | ||
| older_than_days | Yes | ||
| cutoff_saved_at | Yes | ||
| tool | Yes | ||
| limit | Yes | ||
| matched_count | Yes | ||
| pruned_count | Yes | ||
| total_bytes | Yes | ||
| candidates | Yes | ||
| skipped_count | Yes | ||
| skipped | Yes | ||
| message | Yes |
Implementation Reference
- src/tools/jpLitPruneCache.ts:17-72 (handler)The main handler function `createJpLitPruneCacheTool` that implements the prune cache logic: parses input, computes cutoff date, lists cache inventory, filters candidates older than cutoff, optionally removes them (unless dry_run), and returns structured output with counts, bytes, and skipped files.
export function createJpLitPruneCacheTool( baseDir = process.cwd(), now = () => new Date() ) { return async (input: unknown) => { const parsed = pruneCacheInputSchema.parse(input); const cutoff = cutoffFrom(now(), parsed.older_than_days); const inventory = await listCacheInventory(baseDir, parsed.tool); const candidates = inventory.items .filter((item) => item.saved_at < cutoff) .sort((left, right) => left.saved_at.localeCompare(right.saved_at)) .slice(0, parsed.limit); let prunedCount = 0; if (!parsed.dry_run) { for (const candidate of candidates) { await removeInventoryItem(candidate, baseDir); prunedCount += 1; } } const totalBytes = candidates.reduce((sum, item) => sum + item.bytes, 0); const structuredContent: PruneCacheOutput = pruneCacheOutputSchema.parse({ dry_run: parsed.dry_run, older_than_days: parsed.older_than_days, cutoff_saved_at: cutoff, tool: parsed.tool ?? null, limit: parsed.limit, matched_count: candidates.length, pruned_count: prunedCount, total_bytes: totalBytes, candidates: candidates.map(({ tool, cache_key, saved_at, bytes, root }) => ({ tool, cache_key, saved_at, bytes, root })), skipped_count: inventory.skipped.length, skipped: inventory.skipped, message: parsed.dry_run ? `${candidates.length} 件の削除候補があります。削除するには dry_run=false を指定してください。` : `${prunedCount} 件のキャッシュを削除しました。` }); return { content: [ { type: "text" as const, text: JSON.stringify(structuredContent, null, 2) } ], structuredContent }; }; } - Helper function `listCacheInventory` that collects cache files from both 'current' and 'legacy' cache roots, and `removeInventoryItem` that safely deletes a cache file with a path traversal check.
export async function listCacheInventory(baseDir = process.cwd(), tool?: string) { const current = await collectRoot(getCacheRoot(baseDir), "current", tool); const legacy = await collectRoot(getLegacyCacheRoot(baseDir), "legacy", tool); return { items: [...current.items, ...legacy.items], skipped: [...current.skipped, ...legacy.skipped] }; } function isPathInside(parent: string, target: string) { const relative = path.relative(path.resolve(parent), path.resolve(target)); return relative.length > 0 && !relative.startsWith("..") && !path.isAbsolute(relative); } export async function removeInventoryItem( item: CacheInventoryItem, baseDir = process.cwd() ) { const rootPath = item.root === "current" ? getCacheRoot(baseDir) : getLegacyCacheRoot(baseDir); if (!isPathInside(rootPath, item.path)) { throw new Error(`Refusing to remove cache file outside cache root: ${item.path}`); } await rm(item.path, { force: false }); }