Sign in

    moondisk

    Disk usage analysis, snapshot comparison, and safe cleanup planning for MoonBit

    disk
    storage
    snapshot
    cleanup
    cli
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    25 days ago
    Downloads
    13

    #MoonDisk

    MoonDisk is a MoonBit disk-usage analyzer, snapshot comparison engine, and safe cleanup planner. It helps developers understand where a directory grows and review cleanup candidates before any file is moved.

    The project is under active development for the MoonBit September 2026 Hackathon.

    #5-minute quick start

    Install a MoonBit toolchain with moonc 0.10.14 or newer, clone the repository, and run the self-contained demo. Check the compiler with moonc -v. The demo uses deterministic in-memory sample data and does not modify real files:

    git clone https://github.com/xiaobai718-ab/moondisk.git cd moondisk moon update moon run examples/quickstart

    Then analyze a real directory with a read-only scan:

    moon run cmd/main -- scan . --profile developer

    Create a reusable snapshot and a standalone report:

    moon run cmd/main -- snapshot . --profile developer --output workspace.snapshot.json moon run cmd/main -- audit workspace.snapshot.json moon run cmd/main -- report workspace.snapshot.json --output workspace.report.html

    All commands can be run directly with moondisk after installing or copying the compiled executable. While developing from source, prefix commands with moon run cmd/main -- as shown above.

    The example above is the shortest verification path. For the complete filesystem walkthrough using disposable generated data, see DEMO.md. Windows users can run ./scripts/demo.ps1; Linux and macOS users can run ./scripts/demo.sh. The filesystem demo is read-only by default.

    #Design boundary

    MoonDisk analyzes a selected directory. It does not manage partitions, promise physical allocated-block accuracy, recover deleted data, synchronize cloud storage, or permanently delete files. Cleanup operations use a quarantine directory and a recorded restoration manifest.

    #Implemented core

    • Platform-neutral entry and snapshot model
    • Cross-platform relative-path normalization
    • Wildcard ignore matching
    • Recursive directory size aggregation
    • Largest-file and largest-directory ranking
    • Extension and modification-age statistics
    • Empty-directory discovery
    • Snapshot comparison with file and directory deltas
    • Directory growth-hotspot ranking with configurable grouping depth
    • Composition drift by file category and extension, including share movement
    • CI growth policies for net bytes, file counts, individual files, and protected paths
    • Explainable cleanup conditions and built-in rules
    • Cleanup plan construction and stale-source validation
    • Versioned, validated JSON cleanup-rule configurations
    • Quarantine operations with collision-safe restoration manifests
    • Terminal summaries and standalone HTML reports
    • RFC 4180-compatible CSV exports for files and directory totals
    • File-category statistics for source, media, archives, data, and build output
    • Explainable storage insights for dominant directories, old data, and scan gaps
    • Exact duplicate detection using size prefiltering and full byte comparison
    • Multi-snapshot histories with total, peak, average, and per-file growth trends
    • Daily/weekly snapshot-history retention with deterministic compaction plans
    • Storage forecasts, rate volatility, confidence, and capacity-exhaustion estimates
    • Snapshot queries by wildcard, extension, category, size range, and sort order
    • Per-directory storage budgets with warning, exceeded, and missing-path states
    • Cleanup approval policies for risk, volume, rule allowlists, and protected paths
    • Snapshot and quarantine-operation structural audits
    • Native asynchronous directory scanner
    • Scan limits, ignore patterns, hidden-file and symlink policies
    • Runnable analysis, snapshot, planning, apply, restore, and export commands

    #Native commands

    moondisk scan <directory> moondisk scan <directory> --profile quick moondisk init-config --profile developer --output scan.json moondisk scan <directory> --config scan.json moondisk top <directory> --limit 20 moondisk tree <directory> --depth 3 moondisk snapshot <directory> --output snapshot.json moondisk diff <old.json> <new.json> moondisk hotspots <old.json> <new.json> --depth 1 --output hotspots.json moondisk init-rules --output rules.json moondisk plan <directory> --rules rules.json --quarantine <directory> --output plan.json moondisk apply <plan.json> --output operation.json moondisk restore <operation.json> --output restore.json moondisk report <snapshot.json> --output report.html moondisk csv <snapshot.json> --view files --output files.csv moondisk csv <snapshot.json> --view directories --output directories.csv moondisk insights <snapshot.json> moondisk duplicates <directory> --min-size 1048576 --output duplicates.json moondisk history-init <snapshot.json> --output history.json moondisk history-add <history.json> <new-snapshot.json> moondisk history-show <history.json> --limit 10 moondisk init-retention --output retention.json moondisk history-compact <history.json> --config retention.json --output compacted.json moondisk forecast <history.json> --days 30 --capacity 107374182400 --output forecast.json moondisk composition <old.json> <new.json> --by category --output composition.json moondisk init-growth-policy --output growth.json moondisk check-growth <old.json> <new.json> --config growth.json --output evidence.json moondisk find <snapshot.json> --pattern "src/*" --category source-code --extension mbt moondisk init-budgets --output budgets.json moondisk budgets <snapshot.json> --config budgets.json moondisk budgets <snapshot.json> --config budgets.json --fail-on exceeded moondisk init-policy --output policy.json moondisk review-plan <plan.json> --policy policy.json moondisk apply <plan.json> --policy policy.json --output operation.json moondisk audit <snapshot.json> --output audit.json moondisk audit-operation <operation.json>

    The scanner uses moonbitlang/async/fs; the analysis core stays independent of filesystem APIs so it can be tested deterministically and reused by other tools. plan only writes a review artifact. apply scans the root again and refuses to proceed if a planned source changed or disappeared. Successful operations preserve relative paths under quarantine and write a manifest that restore can reverse.

    #Custom cleanup rules

    Start with the built-in rules and edit the generated JSON:

    moondisk init-rules --output team.rules.json moondisk plan ./workspace --rules team.rules.json --output cleanup.plan.json

    Rules compose PathPattern, Extension, MinimumSize, OlderThan, EmptyDirectory, Any, All, and Not conditions. Each match carries a stable rule id, an explanation, a risk level, and whether the item can be regenerated. MoonDisk validates duplicate ids, empty labels, negative limits, and empty condition groups before scanning.

    #Reusable scan policies

    The default, developer, and quick profiles cover full inspection, repository analysis, and fast source-focused scans. init-config writes any profile as JSON so depth, maximum entry count, concurrency, hidden-file behavior, symbolic-link behavior, and ignore patterns can be reviewed and reused. A supplied configuration is validated before filesystem traversal begins.

    #Safety model

    1. Scanning never changes the selected directory.
    2. Rules produce explained candidates rather than deleting anything.
    3. A plan records expected size and modification time.
    4. Apply verifies that every source still matches the plan.
    5. Selected entries move to quarantine while preserving relative paths.
    6. Restore uses the operation manifest and refuses destination collisions.

    #CI storage budgets

    budgets reports every configured path as within budget, near its warning level, exceeded, or missing. Use --fail-on exceeded to make the command fail when a hard limit is crossed, or --fail-on warning to fail for both warnings and hard-limit violations. The default, --fail-on never, only reports results and remains suitable for exploratory use.

    moon run cmd/main -- init-budgets --output team.budgets.json moon run cmd/main -- snapshot . --profile developer --output workspace.snapshot.json moon run cmd/main -- budgets workspace.snapshot.json --config team.budgets.json --fail-on exceeded

    #Development

    moon fmt --check moon check --target native --deny-warn moon test --target native

    CI runs these checks and a read-only command-line smoke test on both Ubuntu and Windows.

    The check-growth command exits with failure when a policy is violated, so its JSON result can serve as CI evidence. History compaction only rewrites the specified history JSON file; it never removes files represented by a snapshot.

    #mooncakes.io publication

    The reusable library and CLI are packaged as xiaobai718-ab/moondisk. Before acceptance, log in to the owning mooncakes.io account and publish version 0.1.0:

    moon package --list moon login moon publish --dry-run moon publish

    See PUBLISHING.md for the preflight and release-tag procedure.

    #License

    Apache-2.0.

    SnapshotCodecError

    pub suberror SnapshotCodecError {
    UnsupportedVersion(Int)
    InvalidSnapshot(String)
    } derive(Eq,
    Debug
    )

    AgeBuckets

    pub(all) struct AgeBuckets {
    day : CategoryStat
    week : CategoryStat
    month : CategoryStat
    quarter : CategoryStat
    year : CategoryStat
    older : CategoryStat
    } derive(Eq,
    Debug
    )

    AuditSeverity

    pub(all) enum AuditSeverity {
    AuditInfo
    AuditWarning
    AuditError
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    BudgetConfig

    pub(all) struct BudgetConfig {
    format_version : Int
    budgets : Array[StorageBudget]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    BudgetConfigIssue

    pub(all) struct BudgetConfigIssue {
    kind : BudgetConfigIssueKind
    budget_id : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    BudgetConfigIssueKind

    pub(all) enum BudgetConfigIssueKind {
    EmptyBudgetId
    DuplicateBudgetId
    InvalidBudgetPath
    InvalidMaximum
    InvalidWarningPercent
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    BudgetFailureThreshold

    pub(all) enum BudgetFailureThreshold {
    NeverFail
    FailOnWarning
    FailOnExceeded
    } derive(Eq,
    Debug
    )

    Controls when a budget evaluation should fail an automated check.

    BudgetResult

    pub(all) struct BudgetResult {
    id : String
    path : String
    maximum_bytes : Int64
    actual_bytes : Int64
    remaining_bytes : Int64
    usage_tenths : Int
    status : BudgetStatus
    description : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    BudgetStatus

    pub(all) enum BudgetStatus {
    WithinBudget
    NearLimit
    OverBudget
    BudgetPathMissing
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    CapacityEstimate

    pub(all) struct CapacityEstimate {
    capacity_bytes : Int64
    current_bytes : Int64
    daily_growth_bytes : Int64
    days_remaining : Int?
    estimated_at : Int64?
    already_exceeded : Bool
    growing : Bool
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Result of estimating when a configured capacity will be reached.

    CategoryStat

    pub(all) struct CategoryStat {
    name : String
    size : Int64
    files : Int
    } derive(Eq,
    Debug
    )

    ChangeKind

    pub(all) enum ChangeKind {
    Added
    Removed
    Grew
    Shrank
    Modified
    KindChanged
    } derive(Eq,
    Debug
    )

    CleanupPlan

    pub(all) struct CleanupPlan {
    format_version : Int
    root : String
    quarantine_root : String
    created_at : Int64
    items : Array[PlanItem]
    issues : Array[PlanIssue]
    reclaimable_size : Int64
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    CleanupPlan::high_risk_count

    fn CleanupPlan::high_risk_count(self : CleanupPlan) -> Int

    CleanupPlan::low_risk_size

    fn CleanupPlan::low_risk_size(self : CleanupPlan) -> Int64

    CleanupRule

    pub(all) struct CleanupRule {
    id : String
    title : String
    reason : String
    risk : RiskLevel
    regenerable : Bool
    condition : RuleCondition
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    CompositionChange

    pub(all) struct CompositionChange {
    name : String
    before_bytes : Int64
    after_bytes : Int64
    byte_delta : Int64
    before_files : Int
    after_files : Int
    file_delta : Int
    share_before_per_mille : Int
    share_after_per_mille : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Change in one file category or extension between two snapshots.

    CompositionDimension

    pub(all) enum CompositionDimension {
    Category
    Extension
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Dimension used when explaining how a directory's contents changed.

    CompositionDrift

    pub(all) struct CompositionDrift {
    dimension : CompositionDimension
    before_total_bytes : Int64
    after_total_bytes : Int64
    net_bytes : Int64
    changes : Array[CompositionChange]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Complete, serializable description of storage composition drift.

    DirectoryChange

    pub(all) struct DirectoryChange {
    path : String
    before_size : Int64
    after_size : Int64
    size_delta : Int64
    } derive(Eq,
    Debug
    )

    DirectoryHotspot

    pub(all) struct DirectoryHotspot {
    path : String
    added_files : Int
    removed_files : Int
    changed_files : Int
    added_bytes : Int64
    removed_bytes : Int64
    grown_bytes : Int64
    shrunk_bytes : Int64
    net_bytes : Int64
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Aggregated file activity for one directory bucket between two snapshots.

    DirectoryStat

    pub(all) struct DirectoryStat {
    path : String
    direct_size : Int64
    recursive_size : Int64
    direct_files : Int
    recursive_files : Int
    } derive(Eq,
    Debug
    )

    DuplicateError

    pub(all) struct DuplicateError {
    path : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    DuplicateGroup

    pub(all) struct DuplicateGroup {
    size : Int64
    paths : Array[String]
    reclaimable_size : Int64
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    DuplicateOptions

    pub(all) struct DuplicateOptions {
    minimum_size : Int64
    maximum_file_size : Int64
    maximum_groups : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    DuplicateOptions::default

    DuplicateResult

    pub(all) struct DuplicateResult {
    groups : Array[DuplicateGroup]
    errors : Array[DuplicateError]
    candidate_files : Int
    compared_files : Int
    reclaimable_size : Int64
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Entry

    pub(all) struct Entry {
    path : String
    kind : EntryKind
    size : Int64
    modified_at : Int64
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    One immutable observation from a directory scan. Paths are stored relative to the scan root with / separators.

    Entry::extension

    fn Entry::extension(self : Entry) -> String

    Entry::is_dir

    fn Entry::is_dir(self : Entry) -> Bool

    Entry::is_file

    fn Entry::is_file(self : Entry) -> Bool

    Entry::name

    fn Entry::name(self : Entry) -> String

    Entry::new

    fn Entry::new(path : String, kind : EntryKind, size : Int64, modified_at : Int64) -> Entry

    Entry::parent

    fn Entry::parent(self : Entry) -> String

    EntryChange

    pub(all) struct EntryChange {
    path : String
    kind : ChangeKind
    before : Entry?
    after : Entry?
    size_delta : Int64
    } derive(Eq,
    Debug
    )

    EntryKind

    pub(all) enum EntryKind {
    Regular
    Directory
    Symlink
    Other
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Kind of an entry observed during a directory scan.

    FileCategory

    pub(all) enum FileCategory {
    SourceCode
    Documents
    Images
    Audio
    Video
    Archives
    Data
    Executables
    Fonts
    Logs
    Temporary
    OtherFiles
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    ForecastConfidence

    pub(all) enum ForecastConfidence {
    Insufficient
    Low
    Medium
    High
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Qualitative stability of a history-based storage projection.

    GrowthPolicy

    pub(all) struct GrowthPolicy {
    format_version : Int
    maximum_net_growth_bytes : Int64
    maximum_added_files : Int
    maximum_removed_files : Int
    maximum_changed_files : Int
    maximum_single_file_growth_bytes : Int64
    protected_patterns : Array[String]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    GrowthPolicy::default

    fn GrowthPolicy::default() -> GrowthPolicy

    GrowthPolicyResult

    pub(all) struct GrowthPolicyResult {
    net_growth_bytes : Int64
    added_files : Int
    removed_files : Int
    changed_files : Int
    largest_file_growth_bytes : Int64
    largest_file_growth_path : String
    violations : Array[GrowthViolation]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    GrowthViolation

    pub(all) struct GrowthViolation {
    kind : GrowthViolationKind
    path : String
    actual : Int64
    limit : Int64
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    GrowthViolationKind

    pub(all) enum GrowthViolationKind {
    NetGrowthExceeded
    AddedFilesExceeded
    RemovedFilesExceeded
    ChangedFilesExceeded
    SingleFileGrowthExceeded
    ProtectedPathChanged
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    HistoryPoint

    pub(all) struct HistoryPoint {
    created_at : Int64
    total_size : Int64
    files : Int
    directories : Int
    scan_errors : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    HistoryRetentionPolicy

    pub(all) struct HistoryRetentionPolicy {
    format_version : Int
    keep_latest : Int
    keep_daily_days : Int
    keep_weekly_weeks : Int
    maximum_observations : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Controls how many historical snapshots remain after compaction. Daily and weekly windows are measured backwards from the newest snapshot.

    HistoryRetentionPolicy::default

    HistorySummary

    pub(all) struct HistorySummary {
    observations : Int
    first_at : Int64
    last_at : Int64
    first_size : Int64
    last_size : Int64
    minimum_size : Int64
    maximum_size : Int64
    net_change : Int64
    average_change : Int64
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    InsightKind

    pub(all) enum InsightKind {
    ScanIncomplete
    VeryLargeFile
    DominantDirectory
    StaleStorage
    RegenerableOutput
    EmptyDirectories
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    InsightOptions

    pub(all) struct InsightOptions {
    large_file_bytes : Int64
    large_file_share_tenths : Int
    dominant_directory_share_tenths : Int
    stale_bytes : Int64
    max_large_file_insights : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    InsightOptions::default

    InsightSeverity

    pub(all) enum InsightSeverity {
    Info
    Notice
    Warning
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    OperationAudit

    pub(all) struct OperationAudit {
    restorable_items : Int
    blocked_items : Int
    ignored_items : Int
    issues : Array[OperationAuditIssue]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    OperationAuditIssue

    pub(all) struct OperationAuditIssue {
    kind : OperationAuditKind
    source : String
    destination : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    OperationAuditKind

    pub(all) enum OperationAuditKind {
    UnsafeOperationPath
    DuplicateOperationSource
    DuplicateOperationDestination
    QuarantineItemMissing
    OriginalPathOccupied
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    OperationItem

    pub(all) struct OperationItem {
    source : String
    destination : String
    size : Int64
    status : OperationStatus
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    OperationManifest

    pub(all) struct OperationManifest {
    format_version : Int
    root : String
    quarantine_root : String
    created_at : Int64
    items : Array[OperationItem]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    OperationManifest::failed_count

    fn OperationManifest::failed_count(self : OperationManifest) -> Int

    OperationManifest::moved_count

    fn OperationManifest::moved_count(self : OperationManifest) -> Int

    OperationManifest::restored_count

    fn OperationManifest::restored_count(self : OperationManifest) -> Int

    OperationStatus

    pub(all) enum OperationStatus {
    Moved
    Restored
    Failed
    Skipped
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    PathTrend

    pub(all) struct PathTrend {
    path : String
    first_size : Int64
    last_size : Int64
    minimum_size : Int64
    maximum_size : Int64
    net_change : Int64
    observations : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    PlanAction

    pub(all) enum PlanAction {
    Quarantine
    Skip
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    PlanIssue

    pub(all) struct PlanIssue {
    kind : PlanIssueKind
    path : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    PlanIssueKind

    pub(all) enum PlanIssueKind {
    DuplicateSource
    CoveredByParent
    DestinationCollision
    SourceMissing
    SourceChanged
    UnsafePath
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    PlanItem

    pub(all) struct PlanItem {
    source : String
    destination : String
    expected_size : Int64
    expected_modified_at : Int64
    action : PlanAction
    rule_id : String
    reason : String
    risk : RiskLevel
    regenerable : Bool
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    PlanPolicy

    pub(all) struct PlanPolicy {
    format_version : Int
    allow_low_risk : Bool
    allow_medium_risk : Bool
    allow_high_risk : Bool
    require_regenerable : Bool
    maximum_items : Int
    maximum_reclaimable_bytes : Int64
    allowed_rule_ids : Array[String]
    protected_patterns : Array[String]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    PlanPolicy::conservative

    fn PlanPolicy::conservative() -> PlanPolicy

    PlanPolicy::permissive

    fn PlanPolicy::permissive() -> PlanPolicy

    PolicyViolation

    pub(all) struct PolicyViolation {
    kind : PolicyViolationKind
    path : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    PolicyViolationKind

    pub(all) enum PolicyViolationKind {
    RiskNotAllowed
    NotRegenerable
    TooManyItems
    ReclaimLimitExceeded
    RuleNotAllowed
    ProtectedPath
    ExistingPlanIssues
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    QuerySort

    pub(all) enum QuerySort {
    SizeDescending
    SizeAscending
    PathAscending
    ModifiedNewest
    ModifiedOldest
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    RetentionDecision

    pub(all) struct RetentionDecision {
    original_observations : Int
    kept_observations : Int
    removed_observations : Int
    kept_created_at : Array[Int64]
    removed_created_at : Array[Int64]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    RiskLevel

    pub(all) enum RiskLevel {
    Low
    Medium
    High
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    RuleCondition

    pub(all) enum RuleCondition {
    PathPattern(String)
    Extension(String)
    MinimumSize(Int64)
    OlderThan(Int64)
    EmptyDirectory
    Any(Array[RuleCondition])
    All(Array[RuleCondition])
    Not(RuleCondition)
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    RuleConfigIssue

    pub(all) struct RuleConfigIssue {
    kind : RuleConfigIssueKind
    rule_id : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    RuleConfigIssueKind

    pub(all) enum RuleConfigIssueKind {
    EmptyId
    DuplicateId
    EmptyTitle
    InvalidMinimumSize
    InvalidAge
    EmptyConditionGroup
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    RuleMatch

    pub(all) struct RuleMatch {
    rule_id : String
    rule_title : String
    path : String
    size : Int64
    reason : String
    risk : RiskLevel
    regenerable : Bool
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    RuleSet

    pub(all) struct RuleSet {
    format_version : Int
    rules : Array[CleanupRule]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    ScanConfig

    pub(all) struct ScanConfig {
    format_version : Int
    options : ScanOptions
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    ScanConfigIssue

    pub(all) struct ScanConfigIssue {
    kind : ScanConfigIssueKind
    value : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    ScanConfigIssueKind

    pub(all) enum ScanConfigIssueKind {
    NegativeDepth
    InvalidEntryLimit
    InvalidConcurrency
    EmptyIgnorePattern
    DuplicateIgnorePattern
    AbsoluteIgnorePattern
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    ScanError

    pub(all) struct ScanError {
    path : String
    operation : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    ScanOptions

    pub(all) struct ScanOptions {
    include_hidden : Bool
    follow_symlinks : Bool
    max_depth : Int
    max_entries : Int
    max_concurrency : Int
    ignore_patterns : Array[String]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    ScanOptions::default

    fn ScanOptions::default() -> ScanOptions

    ScanOptions::with_hidden

    fn ScanOptions::with_hidden(self : ScanOptions, enabled : Bool) -> ScanOptions

    ScanOptions::with_max_depth

    fn ScanOptions::with_max_depth(self : ScanOptions, depth : Int) -> ScanOptions

    ScanOptions::with_max_entries

    fn ScanOptions::with_max_entries(self : ScanOptions, count : Int) -> ScanOptions

    Snapshot

    pub(all) struct Snapshot {
    format_version : Int
    root : String
    created_at : Int64
    entries : Array[Entry]
    errors : Array[ScanError]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Snapshot::directory_count

    fn Snapshot::directory_count(self : Snapshot) -> Int

    Snapshot::file_count

    fn Snapshot::file_count(self : Snapshot) -> Int

    Snapshot::find

    fn Snapshot::find(self : Snapshot, path : String) -> Entry?

    Snapshot::new

    fn Snapshot::new(root : String, created_at : Int64, entries : Array[Entry], errors? : Array[ScanError]) -> Snapshot

    Snapshot::total_size

    fn Snapshot::total_size(self : Snapshot) -> Int64

    SnapshotAudit

    pub(all) struct SnapshotAudit {
    issues : Array[SnapshotAuditIssue]
    errors : Int
    warnings : Int
    information : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    SnapshotAudit::is_usable

    fn SnapshotAudit::is_usable(self : SnapshotAudit) -> Bool

    SnapshotAuditIssue

    pub(all) struct SnapshotAuditIssue {
    kind : SnapshotAuditKind
    severity : AuditSeverity
    path : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    SnapshotAuditKind

    pub(all) enum SnapshotAuditKind {
    InvalidRoot
    InvalidCreationTime
    InvalidEntryPath
    DuplicateEntryPath
    CaseInsensitiveCollision
    NegativeEntrySize
    NonFileHasSize
    MissingParentEntry
    NonDirectoryParent
    EntryOrderMismatch
    InvalidErrorPath
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    SnapshotDiff

    pub(all) struct SnapshotDiff {
    changes : Array[EntryChange]
    directories : Array[DirectoryChange]
    added_size : Int64
    removed_size : Int64
    net_size : Int64
    } derive(Eq,
    Debug
    )

    SnapshotDiff::added_count

    fn SnapshotDiff::added_count(self : SnapshotDiff) -> Int

    SnapshotDiff::changed_count

    fn SnapshotDiff::changed_count(self : SnapshotDiff) -> Int

    SnapshotDiff::removed_count

    fn SnapshotDiff::removed_count(self : SnapshotDiff) -> Int

    SnapshotHistory

    pub(all) struct SnapshotHistory {
    format_version : Int
    root : String
    snapshots : Array[Snapshot]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    SnapshotHistory::new

    fn SnapshotHistory::new(root : String) -> SnapshotHistory

    SnapshotQuery

    pub(all) struct SnapshotQuery {
    path_pattern : String
    extensions : Array[String]
    categories : Array[FileCategory]
    kinds : Array[EntryKind]
    minimum_size : Int64
    maximum_size : Int64
    modified_after : Int64
    modified_before : Int64
    sort : QuerySort
    limit : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    SnapshotQuery::default

    fn SnapshotQuery::default() -> SnapshotQuery

    StorageBudget

    pub(all) struct StorageBudget {
    id : String
    path : String
    maximum_bytes : Int64
    warning_percent : Int
    description : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    StorageForecast

    pub(all) struct StorageForecast {
    observations : Int
    horizon_days : Int
    current_bytes : Int64
    daily_growth_bytes : Int64
    volatility_bytes : Int64
    projected_bytes : Int64
    confidence : ForecastConfidence
    projections : Array[StorageProjection]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Deterministic linear projection derived from snapshot history.

    StorageInsight

    pub(all) struct StorageInsight {
    kind : InsightKind
    severity : InsightSeverity
    title : String
    detail : String
    path : String
    size : Int64
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    StorageProjection

    pub(all) struct StorageProjection {
    created_at : Int64
    projected_bytes : Int64
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    One projected storage observation at a future timestamp.

    age_buckets

    fn age_buckets(snapshot : Snapshot, now : Int64) -> AgeBuckets

    append_history_snapshot

    fn append_history_snapshot(history : SnapshotHistory, snapshot : Snapshot) -> SnapshotHistory raise

    apply_cleanup_plan

    async fn apply_cleanup_plan(plan : CleanupPlan) -> OperationManifest

    audit_operation

    async fn audit_operation(operation : OperationManifest) -> OperationAudit

    audit_severity_name

    fn audit_severity_name(severity : AuditSeverity) -> String

    audit_snapshot

    fn audit_snapshot(snapshot : Snapshot) -> SnapshotAudit

    basename

    fn basename(path : String) -> String

    budget_config_from_json

    fn budget_config_from_json(source : String) -> BudgetConfig raise

    budget_config_to_json

    fn budget_config_to_json(config : BudgetConfig) -> String

    budget_exit_code

    fn budget_exit_code(results : Array[BudgetResult]) -> Int

    budget_failure_threshold_from_name

    fn budget_failure_threshold_from_name(name : String) -> BudgetFailureThreshold raise

    budget_status_name

    fn budget_status_name(status : BudgetStatus) -> String

    budgets_should_fail

    fn budgets_should_fail(results : Array[BudgetResult], threshold : BudgetFailureThreshold) -> Bool

    build_cleanup_plan

    fn build_cleanup_plan(snapshot : Snapshot, matches : Array[RuleMatch], quarantine_root : String, created_at : Int64) -> CleanupPlan

    builtin_cleanup_rules

    fn builtin_cleanup_rules() -> Array[CleanupRule]

    capacity_estimate_to_json

    fn capacity_estimate_to_json(estimate : CapacityEstimate) -> String

    category_stats

    fn category_stats(snapshot : Snapshot) -> Array[CategoryStat]

    compact_history

    fn compact_history(history : SnapshotHistory, decision : RetentionDecision) -> SnapshotHistory

    compare_composition

    fn compare_composition(before : Snapshot, after : Snapshot, dimension : CompositionDimension) -> CompositionDrift

    Compare two snapshots by category or file extension. Entries that disappear remain visible with an after-value of zero, so reports never hide removals.

    compare_snapshots

    fn compare_snapshots(before : Snapshot, after : Snapshot) -> SnapshotDiff

    composition_dimension_from_name

    fn composition_dimension_from_name(name : String) -> CompositionDimension raise

    composition_dimension_name

    fn composition_dimension_name(dimension : CompositionDimension) -> String

    composition_drift_to_json

    fn composition_drift_to_json(drift : CompositionDrift) -> String

    condition_matches

    fn condition_matches(condition : RuleCondition, entry : Entry, snapshot : Snapshot, now : Int64) -> Bool

    default_budget_config

    fn default_budget_config() -> BudgetConfig

    default_rule_set

    fn default_rule_set() -> RuleSet

    default_scan_config

    fn default_scan_config() -> ScanConfig

    developer_scan_config

    fn developer_scan_config() -> ScanConfig

    directory_hotspots

    fn directory_hotspots(diff : SnapshotDiff, depth? : Int) -> Array[DirectoryHotspot]

    Group changed files by the first depth directory segments. A depth of one answers which top-level directory caused the most activity; zero combines all root-relative changes into one bucket.

    directory_hotspots_to_json

    fn directory_hotspots_to_json(hotspots : Array[DirectoryHotspot], pretty? : Bool) -> String

    directory_stats

    fn directory_stats(snapshot : Snapshot) -> Array[DirectoryStat]

    duplicate_result_to_json

    fn duplicate_result_to_json(result : DuplicateResult, pretty? : Bool) -> String

    empty_directories

    fn empty_directories(snapshot : Snapshot) -> Array[Entry]

    entry_matches_query

    fn entry_matches_query(entry : Entry, query : SnapshotQuery) -> Bool

    estimate_capacity

    fn estimate_capacity(history : SnapshotHistory, capacity_bytes : Int64) -> CapacityEstimate raise

    evaluate_budget

    fn evaluate_budget(snapshot : Snapshot, budget : StorageBudget) -> BudgetResult

    evaluate_budgets

    fn evaluate_budgets(snapshot : Snapshot, config : BudgetConfig) -> Array[BudgetResult]

    evaluate_growth_policy

    fn evaluate_growth_policy(before : Snapshot, after : Snapshot, policy : GrowthPolicy) -> GrowthPolicyResult raise

    evaluate_rule

    fn evaluate_rule(rule : CleanupRule, snapshot : Snapshot, now : Int64) -> Array[RuleMatch]

    evaluate_rules

    fn evaluate_rules(rules : Array[CleanupRule], snapshot : Snapshot, now : Int64) -> Array[RuleMatch]

    extension_of

    fn extension_of(path : String) -> String

    extension_stats

    fn extension_stats(snapshot : Snapshot) -> Array[CategoryStat]

    fastest_growing_paths

    fn fastest_growing_paths(history : SnapshotHistory, limit : Int) -> Array[PathTrend]

    file_category

    fn file_category(entry : Entry) -> FileCategory

    file_category_from_key

    fn file_category_from_key(key : String) -> FileCategory raise

    file_category_key

    fn file_category_key(category : FileCategory) -> String

    file_category_name

    fn file_category_name(category : FileCategory) -> String

    files_in_category

    fn files_in_category(snapshot : Snapshot, category : FileCategory) -> Array[Entry]

    find_duplicates

    async fn find_duplicates(snapshot : Snapshot, options? : DuplicateOptions) -> DuplicateResult

    Find exact duplicate files. File size is used as a cheap prefilter; bytes are only read for size buckets containing at least two files. Every reported pair is confirmed using complete byte equality.

    forecast_confidence_name

    fn forecast_confidence_name(confidence : ForecastConfidence) -> String

    forecast_storage

    fn forecast_storage(history : SnapshotHistory, horizon_days : Int) -> StorageForecast raise

    Project one point per future day using the mean rate between actual observations. No wall-clock access or floating-point rounding is involved.

    format_bytes

    fn format_bytes(value : Int64) -> String

    format_percent

    fn format_percent(part : Int64, total : Int64) -> String

    growth_policy_from_json

    fn growth_policy_from_json(source : String) -> GrowthPolicy raise

    growth_policy_result_to_json

    fn growth_policy_result_to_json(result : GrowthPolicyResult) -> String

    growth_policy_to_json

    fn growth_policy_to_json(policy : GrowthPolicy) -> String

    history_from_json

    fn history_from_json(source : String) -> SnapshotHistory raise

    history_from_snapshot

    fn history_from_snapshot(snapshot : Snapshot) -> SnapshotHistory

    history_points

    fn history_points(history : SnapshotHistory) -> Array[HistoryPoint]

    history_to_json

    fn history_to_json(history : SnapshotHistory) -> String

    is_descendant

    fn is_descendant(path : String, parent : String) -> Bool

    is_hidden_path

    fn is_hidden_path(path : String) -> Bool

    largest_directories

    fn largest_directories(snapshot : Snapshot, limit : Int) -> Array[DirectoryStat]

    largest_files

    fn largest_files(snapshot : Snapshot, limit : Int) -> Array[Entry]

    matches_any_pattern

    fn matches_any_pattern(path : String, patterns : Array[String]) -> Bool

    normalize_relative_path

    fn normalize_relative_path(input : String) -> String

    operation_audit_to_json

    fn operation_audit_to_json(audit : OperationAudit) -> String

    operation_from_json

    fn operation_from_json(source : String) -> OperationManifest raise

    operation_to_json

    fn operation_to_json(operation : OperationManifest) -> String

    pad_left

    fn pad_left(text : String, width : Int) -> String

    pad_right

    fn pad_right(text : String, width : Int) -> String

    parent_path

    fn parent_path(path : String) -> String

    path_depth

    fn path_depth(path : String) -> Int

    fn path_trends(history : SnapshotHistory) -> Array[PathTrend]

    plan_from_json

    fn plan_from_json(source : String) -> CleanupPlan raise

    plan_history_retention

    fn plan_history_retention(history : SnapshotHistory, policy : HistoryRetentionPolicy) -> RetentionDecision raise

    Select snapshots deterministically. The newest observations are protected first, followed by one newest observation for each daily and weekly bucket.

    plan_policy_from_json

    fn plan_policy_from_json(source : String) -> PlanPolicy raise

    plan_policy_to_json

    fn plan_policy_to_json(policy : PlanPolicy) -> String

    plan_to_json

    fn plan_to_json(plan : CleanupPlan, pretty? : Bool) -> String

    query_snapshot

    fn query_snapshot(snapshot : Snapshot, query : SnapshotQuery) -> Array[Entry]

    query_sort_from_name

    fn query_sort_from_name(name : String) -> QuerySort raise

    query_total_size

    fn query_total_size(entries : Array[Entry]) -> Int64

    quick_scan_config

    fn quick_scan_config() -> ScanConfig

    read_budget_config

    async fn read_budget_config(path : String) -> BudgetConfig

    read_growth_policy

    async fn read_growth_policy(path : String) -> GrowthPolicy

    read_history

    async fn read_history(path : String) -> SnapshotHistory

    read_operation

    async fn read_operation(path : String) -> OperationManifest

    read_plan

    async fn read_plan(path : String) -> CleanupPlan

    read_plan_policy

    async fn read_plan_policy(path : String) -> PlanPolicy

    read_retention_policy

    async fn read_retention_policy(path : String) -> HistoryRetentionPolicy

    read_rule_set

    async fn read_rule_set(path : String) -> RuleSet

    read_scan_config

    async fn read_scan_config(path : String) -> ScanConfig

    read_snapshot

    async fn read_snapshot(path : String) -> Snapshot

    render_budgets

    fn render_budgets(results : Array[BudgetResult]) -> String

    render_composition_drift

    fn render_composition_drift(drift : CompositionDrift, limit? : Int) -> String

    render_diff_csv

    fn render_diff_csv(diff : SnapshotDiff) -> String

    render_diff_summary

    fn render_diff_summary(diff : SnapshotDiff, limit? : Int) -> String

    render_directory_csv

    fn render_directory_csv(snapshot : Snapshot) -> String

    render_directory_hotspots

    fn render_directory_hotspots(hotspots : Array[DirectoryHotspot], limit? : Int) -> String

    render_duplicates

    fn render_duplicates(result : DuplicateResult) -> String

    render_growth_policy_result

    fn render_growth_policy_result(result : GrowthPolicyResult) -> String

    render_history

    fn render_history(history : SnapshotHistory, limit? : Int) -> String

    render_html_report

    fn render_html_report(snapshot : Snapshot, title? : String) -> String

    render_insights

    fn render_insights(insights : Array[StorageInsight]) -> String

    render_operation_audit

    fn render_operation_audit(audit : OperationAudit) -> String

    render_plan

    fn render_plan(plan : CleanupPlan) -> String

    render_plan_csv

    fn render_plan_csv(plan : CleanupPlan) -> String

    render_policy_review

    fn render_policy_review(violations : Array[PolicyViolation]) -> String

    render_query_results

    fn render_query_results(entries : Array[Entry]) -> String

    render_retention_decision

    fn render_retention_decision(decision : RetentionDecision) -> String

    render_snapshot_audit

    fn render_snapshot_audit(audit : SnapshotAudit) -> String

    render_snapshot_csv

    fn render_snapshot_csv(snapshot : Snapshot) -> String

    render_storage_forecast

    fn render_storage_forecast(forecast : StorageForecast, capacity? : CapacityEstimate?) -> String

    render_summary

    fn render_summary(snapshot : Snapshot, limit? : Int) -> String

    render_tree

    fn render_tree(snapshot : Snapshot, max_depth? : Int, minimum_size? : Int64) -> String

    restore_operation

    async fn restore_operation(operation : OperationManifest) -> OperationManifest

    retention_policy_from_json

    fn retention_policy_from_json(source : String) -> HistoryRetentionPolicy raise

    retention_policy_to_json

    fn retention_policy_to_json(policy : HistoryRetentionPolicy) -> String

    review_plan_policy

    fn review_plan_policy(plan : CleanupPlan, policy : PlanPolicy) -> Array[PolicyViolation]

    risk_name

    fn risk_name(risk : RiskLevel) -> String

    rule_set_from_json

    fn rule_set_from_json(source : String) -> RuleSet raise

    rule_set_to_json

    fn rule_set_to_json(rule_set : RuleSet, pretty? : Bool) -> String

    scan_config_for_profile

    fn scan_config_for_profile(profile : String) -> ScanConfig raise

    scan_config_from_json

    fn scan_config_from_json(source : String) -> ScanConfig raise

    scan_config_to_json

    fn scan_config_to_json(config : ScanConfig, pretty? : Bool) -> String

    scan_directory

    async fn scan_directory(root : String, created_at : Int64, options? : ScanOptions) -> Snapshot

    Scan a real directory using MoonBit's native asynchronous filesystem API. Symbolic links are recorded but not followed unless the caller explicitly opts in.

    snapshot_audit_to_json

    fn snapshot_audit_to_json(audit : SnapshotAudit) -> String

    snapshot_from_json

    fn snapshot_from_json(source : String) -> Snapshot raise

    snapshot_to_json

    fn snapshot_to_json(snapshot : Snapshot, pretty? : Bool) -> String

    stat_for_directory

    fn stat_for_directory(snapshot : Snapshot, path : String) -> DirectoryStat?

    storage_forecast_to_json

    fn storage_forecast_to_json(forecast : StorageForecast) -> String

    storage_insights

    fn storage_insights(snapshot : Snapshot, now : Int64, options? : InsightOptions) -> Array[StorageInsight]

    summarize_history

    fn summarize_history(history : SnapshotHistory) -> HistorySummary?

    validate_budget_config

    fn validate_budget_config(config : BudgetConfig) -> Array[BudgetConfigIssue]

    validate_growth_policy

    fn validate_growth_policy(policy : GrowthPolicy) -> Array[String]

    validate_plan_against_snapshot

    fn validate_plan_against_snapshot(plan : CleanupPlan, current : Snapshot) -> Array[PlanIssue]

    validate_retention_policy

    fn validate_retention_policy(policy : HistoryRetentionPolicy) -> Array[String]

    validate_rule_set

    fn validate_rule_set(rule_set : RuleSet) -> Array[RuleConfigIssue]

    validate_scan_config

    fn validate_scan_config(config : ScanConfig) -> Array[ScanConfigIssue]

    wildcard_match

    fn wildcard_match(pattern : String, text : String) -> Bool

    write_budget_config

    async fn write_budget_config(path : String, config : BudgetConfig) -> Unit

    write_growth_policy

    async fn write_growth_policy(path : String, policy : GrowthPolicy) -> Unit

    write_history

    async fn write_history(path : String, history : SnapshotHistory) -> Unit

    write_operation

    async fn write_operation(path : String, operation : OperationManifest) -> Unit

    write_plan

    async fn write_plan(path : String, plan : CleanupPlan) -> Unit

    write_plan_policy

    async fn write_plan_policy(path : String, policy : PlanPolicy) -> Unit

    write_retention_policy

    async fn write_retention_policy(path : String, policy : HistoryRetentionPolicy) -> Unit

    write_rule_set

    async fn write_rule_set(path : String, rule_set : RuleSet) -> Unit

    write_scan_config

    async fn write_scan_config(path : String, config : ScanConfig) -> Unit

    write_snapshot

    async fn write_snapshot(path : String, snapshot : Snapshot) -> Unit

    write_text_file

    async fn write_text_file(path : String, content : String) -> Unit