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.
///
/// Line numbers are 1-based to match function-info / class-info / comment-info.
/// Columns are 0-based (tree-sitter byte column).
interface tree-query {
/// A single capture from a query match.
/// `start-line` / `end-line` are 1-based.
/// `start-col` / `end-col` are 0-based byte columns.
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;
/// Find the function in `path` whose body contains the given (line, col)
/// position. Lines are 1-based, columns are 0-based — same convention as
/// tree-query. Returns the innermost matching function (smallest line range).
/// Plugins typically pass `input.path` here.
function-at: func(path: string, line: u32, col: u32) -> option<function-info>;
}
/// 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>;
}