cha-core 1.16.0

Core analysis engine for Cha — pluggable code smell detection
Documentation
package cha:plugin@0.3.0;

/// Data types shared between host and guest.
interface types {
    /// Severity level.
    enum severity {
        hint,
        warning,
        error,
    }

    /// Smell category.
    enum smell-category {
        bloaters,
        oo-abusers,
        change-preventers,
        dispensables,
        couplers,
        security,
    }

    /// Source location.
    record location {
        path: string,
        start-line: u32,
        start-col: u32,
        end-line: u32,
        end-col: u32,
        name: option<string>,
    }

    /// Where a referenced type is declared.
    variant type-origin {
        /// Declared inside the project.
        project-local,
        /// Imported from external module / crate / package (name if known).
        external(string),
        /// Built-in primitive / standard library scalar.
        primitive,
        /// Could not be resolved.
        unknown,
    }

    /// A function parameter or return type, with resolved origin.
    record type-ref {
        /// Innermost type identifier after stripping refs/generics/containers.
        name: string,
        /// Original source text as written.
        raw: string,
        /// Where the type is declared.
        origin: type-origin,
    }

    /// A single finding.
    record finding {
        smell-name: string,
        category: smell-category,
        severity: severity,
        location: location,
        message: string,
        suggested-refactorings: list<string>,
        actual-value: option<f64>,
        threshold: option<f64>,
    }

    /// Switch/match arm value type.
    variant arm-value {
        str-lit(string),
        int-lit(s64),
        char-lit(u32),
        other,
    }

    /// Function info from parsed source.
    record function-info {
        name: string,
        start-line: u32,
        end-line: u32,
        name-col: u32,
        name-end-col: u32,
        line-count: u32,
        complexity: u32,
        parameter-count: u32,
        parameter-types: list<type-ref>,
        parameter-names: list<string>,
        chain-depth: u32,
        switch-arms: u32,
        switch-arm-values: list<arm-value>,
        external-refs: list<string>,
        is-delegating: bool,
        is-exported: bool,
        comment-lines: u32,
        referenced-fields: list<string>,
        null-check-fields: list<string>,
        switch-dispatch-target: option<string>,
        optional-param-count: u32,
        called-functions: list<string>,
        cognitive-complexity: u32,
        body-hash: option<string>,
        return-type: option<type-ref>,
    }

    /// Class info from parsed source.
    record class-info {
        name: string,
        start-line: u32,
        end-line: u32,
        name-col: u32,
        name-end-col: u32,
        method-count: u32,
        line-count: u32,
        delegating-method-count: u32,
        field-count: u32,
        field-names: list<string>,
        field-types: list<string>,
        is-exported: bool,
        has-behavior: bool,
        is-interface: bool,
        parent-name: option<string>,
        override-count: u32,
        self-call-count: u32,
        has-listener-field: bool,
        has-notify-method: bool,
    }

    /// Import info.
    record import-info {
        source: string,
        line: u32,
        col: u32,
        is-module-decl: bool,
    }

    /// File role inferred by the host from path patterns.
    enum file-role {
        source,
        test,
        doc,
        config,
        generated,
    }

    /// Comment info from parsed source.
    record comment-info {
        text: string,
        line: u32,
    }

    /// Typed option value for plugin configuration.
    variant option-value {
        str(string),
        int(s64),
        float(f64),
        boolean(bool),
        list-str(list<string>),
    }

    /// Full analysis context passed to the plugin.
    record analysis-input {
        path: string,
        content: string,
        language: string,
        total-lines: u32,
        role: file-role,
        functions: list<function-info>,
        classes: list<class-info>,
        imports: list<import-info>,
        comments: list<comment-info>,
        type-aliases: list<tuple<string, string>>,
        options: list<tuple<string, option-value>>,
    }
}

/// AST query interface provided by the host.
/// Plugins can import this to execute tree-sitter queries against the current file's AST.
interface tree-query {
    /// A single capture from a query match.
    record query-match {
        capture-name: string,
        node-kind: string,
        text: string,
        start-line: u32,
        start-col: u32,
        end-line: u32,
        end-col: u32,
    }

    /// Execute a tree-sitter query pattern against the current file's AST.
    /// Returns list of matches; each match is a list of captures.
    run-query: func(pattern: string) -> list<list<query-match>>;

    /// Execute multiple query patterns in one call, reducing cross-boundary overhead.
    /// Returns results in the same order as the input patterns.
    run-queries: func(patterns: list<string>) -> list<list<list<query-match>>>;

    /// Get the AST node at a specific position.
    node-at: func(line: u32, col: u32) -> option<query-match>;

    /// Get all named top-level child nodes within a line range.
    nodes-in-range: func(start-line: u32, end-line: u32) -> list<query-match>;
}

/// Project-wide query interface provided by the host.
/// Plugins call these methods to access cross-file data without needing
/// the entire project model serialized over the WASM boundary.
interface project-query {
    use types.{type-ref, function-info};

    /// True if `name` is called from any file other than `exclude-path`.
    is-called-externally: func(name: string, exclude-path: string) -> bool;

    /// All paths (as strings) that reference `name`.
    callers-of: func(name: string) -> list<string>;

    /// Cross-file call counts: each tuple is (caller-path, callee-path, count).
    cross-file-call-counts: func() -> list<tuple<string, string, u32>>;

    /// First file (as string) that declared this function.
    function-home: func(name: string) -> option<string>;

    /// First (file, function-info) tuple for this function name.
    function-by-name: func(name: string) -> option<tuple<string, function-info>>;

    /// First file (as string) that declared this class/struct.
    class-home: func(name: string) -> option<string>;

    /// True if `name` is declared somewhere in the project.
    is-project-type: func(name: string) -> bool;

    /// True if the type is a genuine third-party dependency
    /// (External origin AND not stdlib AND not workspace sibling).
    is-third-party: func(type-ref: type-ref) -> bool;

    /// Workspace sibling crate names (Rust workspace) — empty otherwise.
    workspace-crate-names: func() -> list<string>;

    /// True if path looks like a test file or sits inside a test directory.
    is-test-path: func(path: string) -> bool;

    /// Total count of analyzed files.
    file-count: func() -> u32;
}

/// The world that WASM plugins must implement.
world analyzer {
    use types.{analysis-input, finding};

    import tree-query;
    import project-query;

    /// Return the plugin name (matches [plugins.<name>] in .cha.toml).
    export name: func() -> string;

    /// Return the plugin version (e.g. "1.0.0").
    export version: func() -> string;

    /// Return a short description of what the plugin detects.
    export description: func() -> string;

    /// Return the list of authors.
    export authors: func() -> list<string>;

    /// Return the smell names this plugin can produce. Used by the host for
    /// smell-level filtering, `cha plugin list`, and docs. May be empty.
    export smells: func() -> list<string>;

    /// Run analysis and return findings.
    export analyze: func(input: analysis-input) -> list<finding>;
}