Sign in

    moonguard

    Preview, validate and recover bounded AI file operations with a shared MoonBit core.

    file-operations
    preview
    recovery
    validation
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    13 hours ago
    Downloads
    4

    #MoonGuard

    Preview, validate and recover bounded file-operation plans with a pure MoonBit core. The core performs no I/O. Hosts supply accurate snapshots, check filesystem types, serialize writers, persist a journal before writing, and apply only validated changes.

    Core module: icyaaaww/moonguard@0.1.0. The local application remains version 1.0.0; library and application versions are independent. The current publishing tool requires a 0.x.y library version.

    #Install

    In a MoonBit project:

    moon add icyaaaww/moonguard

    Import the root package in moon.pkg:

    ///|
    import {
    "icyaaaww/moonguard",
    }

    The following API example is checked by the library tests:

    ///|
    test {
    let before : @moonguard.Snapshot = {
    files: [{ path: "note.md", content: "draft\n", }],
    folders: [],
    blocked: [],
    }
    let planned = @moonguard.plan(before, [
    { kind: "write", path: "note.md", to: None, content: Some("reviewed\n"), },
    ])
    assert_true(planned.ready())
    assert_true(@moonguard.check_apply(planned, before).is_empty())
    let recovery = @moonguard.restore_plan(planned, planned.result)
    assert_true(recovery.ready())
    assert_eq(recovery.result.files, before.files)
    }

    plan(snapshot, operations) computes sorted net changes. Invalid operations invalidate the entire plan. check_apply(plan, current) rebuilds the plan and rejects stale input. restore_plan(plan, current) rejects subsequent edits to affected paths; unrelated new files survive. Use recovery=true only for an interrupted commit/restore, where affected paths may have either their before or after contents.

    organize(snapshot) is a deterministic Markdown demonstration planner. The local Node adapter and browser demo are available in the GitHub repository. No LLM is bundled. This package is not an operating-system sandbox or a general transaction engine.

    #Package contents and verification

    The registry archive contains the reusable core, JSON bridge, tests, a runnable cmd/main example, architecture notes and Apache-2.0 license. The Node filesystem executor, browser UI and MCP adapter are separate hosts in the GitHub repository; installing the library does not install or run them.

    moon check --target all moon test --target js moon test --target wasm-gc moon run cmd/main --target js

    See docs/architecture.md for limits and the filesystem obligations of a host. Do not treat a ready plan as authorization to mutate files without rechecking the current snapshot and protecting against concurrent writers and unsafe file types.

    Change

    pub(all) struct Change {
    path : String
    before : String?
    after : String?
    } derive(Eq,
    Debug
    )

    A net path change; None means that the file is absent.
    impl ToJson for Change

    Change::equal

    fn Change::equal(Change, Change) -> Bool

    Change::not_equal

    fn Change::not_equal(x : Change, y : Change) -> Bool

    Change::to_json

    fn Change::to_json(self : Change) -> Json

    Change::to_repr

    File

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

    A UTF-8 text file. Paths are portable, workspace-relative paths.

    File::equal

    fn File::equal(File, File) -> Bool

    File::not_equal

    fn File::not_equal(x : File, y : File) -> Bool

    File::to_json

    fn File::to_json(File) -> Json

    File::to_repr

    Issue

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

    Issue::equal

    fn Issue::equal(Issue, Issue) -> Bool

    Issue::not_equal

    fn Issue::not_equal(x : Issue, y : Issue) -> Bool

    Issue::to_json

    fn Issue::to_json(Issue) -> Json

    Issue::to_repr

    Operation

    pub(all) struct Operation {
    kind : String
    path : String
    to : String?
    content : String?
    } derive(Eq,
    Debug
    )

    Supported kinds: create, write, move and trash (recoverable removal).
    impl ToJson for Operation

    Operation::equal

    fn Operation::equal(Operation, Operation) -> Bool

    Operation::not_equal

    fn Operation::not_equal(x : Operation, y : Operation) -> Bool

    Operation::to_json

    fn Operation::to_json(self : Operation) -> Json

    Plan

    pub(all) struct Plan {
    schema : Int
    title : String
    baseline : Snapshot
    result : Snapshot
    operations : Array[Operation]
    changes : Array[Change]
    issues : Array[Issue]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    An immutable-by-convention plan. Hosts must recompile untrusted plans.

    Plan::equal

    fn Plan::equal(Plan, Plan) -> Bool

    Plan::not_equal

    fn Plan::not_equal(x : Plan, y : Plan) -> Bool

    Plan::ready

    fn Plan::ready(self : Plan) -> Bool

    Plan::to_json

    fn Plan::to_json(Plan) -> Json

    Plan::to_repr

    Snapshot

    pub(all) struct Snapshot {
    files : Array[File]
    folders : Array[String]
    blocked : Array[String]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Opaque files and symlinks appear in blocked; directories in folders.

    Snapshot::equal

    fn Snapshot::equal(Snapshot, Snapshot) -> Bool

    Snapshot::not_equal

    fn Snapshot::not_equal(x : Snapshot, y : Snapshot) -> Bool

    Snapshot::to_json

    fn Snapshot::to_json(Snapshot) -> Json

    Snapshot::to_repr

    check_apply

    fn check_apply(planned : Plan, current : Snapshot) -> Array[Issue]

    Reject a stale plan using its whole read snapshot. Hosts must also check real filesystem types and serialize their own writers at the I/O boundary.

    differences

    fn differences(before : Snapshot, after : Snapshot) -> Array[Change]

    Sorted net differences, suitable for verification and recovery journals.

    empty_snapshot

    fn empty_snapshot() -> Snapshot

    organize

    fn organize(snapshot : Snapshot) -> Plan

    Deterministic example planner, not an LLM. Root Markdown files declaring category: engineering/product/research move under docs/. External AI callers can instead submit structured operations to plan().

    plan

    fn plan(snapshot : Snapshot, operations : Array[Operation], title? : String) -> Plan

    Build a plan without changing the input. An invalid operation invalidates the entire plan; partial previews are never returned as executable results.

    restore_plan

    fn restore_plan(planned : Plan, current : Snapshot, recovery? : Bool) -> Plan

    Construct a restore plan. Unrelated new files survive; modified touched files cause a conflict. recovery=true permits either before or after values for an interrupted commit/restore, without overwriting a third value.
    fn rewrite_links(content : String, old_path : String, new_path : String, moved : Map[String, String]) -> String

    Rewrite simple inline relative Markdown links, including fragments. Fenced code and inline code are preserved. This is a documented subset, not a CommonMark parser; encoded URLs and reference links are unchanged.

    valid_path

    fn valid_path(path : String) -> Bool

    Reject ambiguous paths rather than silently normalizing a requested action.

    test {
    assert_true(@moonguard.valid_path("notes/设计.md"))
    assert_false(@moonguard.valid_path("../private.txt"))
    }

    validate_snapshot

    fn validate_snapshot(snapshot : Snapshot) -> Array[Issue]

    Full snapshot validation is shared by all frontends.