UNPKG

akm-cli

Version:

akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur

93 lines (92 loc) • 4.65 kB
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. /** * `memory-cleanup-archive` advisory for `akm health` (item 4, 0.9.17-alpha.9 * plan §5.4/§8 step 8). * * Reports the archive's size and file count for every bundle, git-backed or * not. A bundle with no `.git` of its own keeps every retirement's archived * bytes forever (the purge sweep never runs there at all), so that alone is * reported. A git-backed bundle can ALSO have bytes the purge sweep will * never remove: `.git` presence alone does not prove a retirement was ever * committed (`proposal accept` only commits for a `kind: "git"` write * target, and a `kind: "filesystem"` bundle that merely happens to have a * `.git` directory never gets one) — those bytes sit there indefinitely, * however old they get, with nothing else to say so. This checks the SAME * git state `purgeGracedArchive` checks (tracked, clean, verifiable) and * reports how much of the archive fails it. * * Silent whenever there is nothing to say: the archive does not exist or is * empty, or (for a git-backed bundle) every byte in it is purgeable once it * ages out. */ import fs from "node:fs"; import path from "node:path"; import { MEMORY_ARCHIVE_REL } from "../../core/asset/memory-archive.js"; import { toPosix } from "../../core/common.js"; import { checkGitPathSafety, isGitBackedStash } from "../../sources/providers/git-stash.js"; import { MAX_WALK_ENTRIES, sizeOfPath } from "./data-dir-usage.js"; /** * Build the `memory-cleanup-archive` advisory, or `undefined` when there is * nothing to report. */ export function collectArchiveUsageAdvisory(stashDir) { const archiveRoot = path.join(stashDir, MEMORY_ARCHIVE_REL); if (!fs.existsSync(archiveRoot)) return undefined; if (!isGitBackedStash(stashDir)) { const usage = sizeOfPath(archiveRoot, { remaining: MAX_WALK_ENTRIES }); if (usage.files === 0) return undefined; const lowerBound = usage.truncated ? ` (a lower bound — the walk stopped after ${MAX_WALK_ENTRIES} entries)` : ""; return { name: "memory-cleanup-archive", kind: "deterministic", status: "pass", confidence: "high", message: `${usage.files} archived file(s), ${usage.bytes} byte(s)${lowerBound} in .akm/memory-cleanup/archive — ` + "this bundle has no git history, so the purge sweep leaves it untouched.", evidence: { files: usage.files, bytes: usage.bytes, truncated: usage.truncated }, }; } // Git-backed: the SAME three checks purgeGracedArchive runs (B1, G10) — // computed once here, not per file, and reused via `onFile` below instead // of a second walk of the same tree. A failed git check here fails the // same way purgeGracedArchive's own sweep would: nothing in the archive // can be proven purgeable, so every byte counts as unpurgeable rather // than guessing (`checkGitPathSafety`'s `isSafe` is always `false` when // `ok` is `false`). const gitSafety = checkGitPathSafety(stashDir, MEMORY_ARCHIVE_REL); let unpurgeableFiles = 0; let unpurgeableBytes = 0; const usage = sizeOfPath(archiveRoot, { remaining: MAX_WALK_ENTRIES }, (filePath, bytes) => { const key = toPosix(path.relative(stashDir, filePath)); if (!gitSafety.isSafe(key)) { unpurgeableFiles++; unpurgeableBytes += bytes; } }); if (usage.files === 0) return undefined; if (unpurgeableFiles === 0) return undefined; // everything here is purgeable once it ages out — nothing to say const lowerBound = usage.truncated ? ` (a lower bound — the walk stopped after ${MAX_WALK_ENTRIES} entries)` : ""; return { name: "memory-cleanup-archive", kind: "deterministic", status: "warn", confidence: "high", message: `${usage.files} archived file(s), ${usage.bytes} byte(s)${lowerBound} in .akm/memory-cleanup/archive; ` + `${unpurgeableFiles} file(s), ${unpurgeableBytes} byte(s) of that cannot be purged (untracked, modified, ` + "or unverifiable in git) — commit them so the purge sweep can remove them once they age out.", evidence: { files: usage.files, bytes: usage.bytes, truncated: usage.truncated, unpurgeableFiles, unpurgeableBytes, gitStateKnown: gitSafety.ok, }, }; }